Add externs to avoid multiple definitions, and then add missing definitions.
[rrq/maintain_lilo.git] / diagnose / test4.c
1 /* test4.c */
2 /*
3  Copyright 2001-2005 John Coffman.
4  All rights reserved.
5
6  Licensed under the terms contained in the file 'COPYING' in the LILO
7  source directory.
8
9 */
10 #include <bios.h>
11 #include "../src/bdata.h"
12
13 #define DISK_VERSION "2.4"
14
15 #if __MSDOS__==0
16 #define putch bios_putc
17 #define getch bios_getc
18 #define printf cprintf
19 #define CR 13
20 #else
21 #include <stdio.h>
22 #define putch(c) fputc((c),stdout)
23 #define getch getchar
24 #define CR 10
25 #endif
26
27 #define CTRL_C 03
28 #define SPACER "\f\n"
29 #define SEQ 0x3C4
30
31 #define FS_REG 0x80
32
33 #ifndef EDD_SUBSET
34 #define EDD_SUBSET 4
35 #define EDD_LOCK 2
36 #define EDD_PACKET 1
37 #endif
38 #ifndef SECTOR_SIZE
39 #define SECTOR_SIZE 512
40 #endif
41 #define nelem(a) (sizeof(a)/sizeof((a)[0]))
42
43 typedef unsigned char  byte;
44 typedef unsigned short word;
45 typedef unsigned long dword;
46
47 extern union REGS   __argr;
48 extern struct SREGS __argseg;
49
50 union REGS reg, oreg;
51 struct SREGS sreg;
52 int last_good_disk;
53 int video_1 = 0xF00;
54 int fs_mod = 0;
55 int num_hd = BD_MAX_HARD;
56 int errno;
57 dword hma;      /* highest memory address */
58 struct {
59   long  start, start_hi,
60         length, length_hi,
61         mtype;
62     } mem_map;
63 #define E820_MAGIC 0x534D4150
64
65 struct gdt_entry {
66         unsigned short limit;
67         unsigned short base01;
68         unsigned char  base2;
69         unsigned char  dtype;   /* 0x93 for data */
70         unsigned char  limit2;  /* limit in low nibble, granularity & 32-bit in high nibble */
71         unsigned char  base3;
72 };
73
74 struct gdt_entry gdt[6];
75
76 static
77 int get_fs(void)
78 {
79 #asm
80  mov ax,fs
81 #endasm
82 }
83
84 static
85 int set_fs(int val)
86 {
87     int i = val;
88 #asm
89  mov ax,4[bp]
90  mov fs,ax
91 #endasm
92     return i;
93 }
94
95 static
96 int check_fs(void)
97 {
98     int ret = 0;
99
100 #if DEBUG>=2    
101     printf("#");
102 #endif    
103     if (get_fs() != FS_REG) {
104         fs_mod = ret = 1;
105         printf("\nThe FS register has been modified.\n");
106         set_fs(FS_REG);
107     }
108     return ret;
109 }
110
111 static
112 int hicopy (unsigned long to, unsigned long from, int wcount)
113 {
114     int status;
115     unsigned char save;
116     
117     memset(gdt, 0, sizeof(gdt));
118     gdt[2].limit = gdt[3].limit = 0xFFFF;
119     gdt[2].dtype = gdt[3].dtype = 0x93;
120     
121     gdt[2].base01 = from;
122     gdt[2].base2 = from>>16;
123     gdt[2].base3 = from>>24;
124     
125     gdt[3].base01 = to;
126     gdt[3].base2 = to>>16;
127     save = gdt[3].base3 = to>>24;
128     
129     segread(&sreg);
130     sreg.es = sreg.ds;
131     reg.h.ah = 0x87;
132     reg.x.cx = wcount;
133     reg.x.si = gdt;
134 /***    gdt[3].base3 &= 0;   / crosstalk */
135     int86x(0x15, &reg, &oreg, &sreg);
136     
137     status = oreg.h.ah;
138     if (oreg.x.cflag) status |= 0x100;
139     if (save!=gdt[3].base3) status |= 0x200;
140     errno |= status;
141     return status;
142 }
143
144 unsigned long linear(void *ptr)
145 {
146     segread(&sreg);
147     return ((unsigned long)sreg.ds<<4) + (unsigned int)ptr;
148 }
149
150 word hipeekw(long address)
151 {
152     word temp;
153     hicopy(linear(&temp), address, 1);
154     return temp;
155 }
156
157 int hipokew(long address, word value)
158 {
159     return hicopy(address, linear(&value), 1);
160 }
161
162 #if __MSDOS__==0
163 static
164 bios_putc0(int c)
165 {
166     union REGS reg;
167     if (c=='\f') {
168 #if 0
169         reg.h.ah = 0x0F;
170         int86(0x10, &reg, &reg);
171         reg.h.ah = 0;
172         int86(0x10, &reg, &reg);
173 #else
174         static word upper = 0;
175         if (!upper) {
176             __set_es(0x40);     /* address BIOS data area */
177             upper = __peek_es(0x84);
178             if (upper < 24 || upper > 50) upper = 24;
179             upper <<= 8;
180             reg.h.ah = 0x0F;    /* get video mode */
181             int86(0x10, &reg, &reg);
182             upper |= (reg.h.ah-1);
183         }
184         reg.x.ax = 0x0600;      /* blank screen area */
185         reg.h.bh = 7;
186         reg.x.cx = 0x0000;
187         reg.x.dx = upper;
188         int86(0x10, &reg, &reg);
189         reg.h.ah = 2;           /* set cursor position */
190         reg.h.bh = 0;
191         reg.x.dx = 0x0000;
192         int86(0x10, &reg, &reg);
193 #endif
194     } else {
195         reg.h.al = c;
196         reg.h.ah = 14;
197         reg.x.bx = 7;
198         int86(0x10, &reg, &reg);
199     }
200 }
201
202 void bios_putc(char c)
203 {
204 static int col;
205    
206     switch(c) {
207     case '\t':
208         do bios_putc(' '); while(col&7);
209         break;
210     case '\n':  bios_putc0('\r');
211         /* fall into CR */
212     case '\f':
213     case '\r':  col=0;
214     default:
215         bios_putc0(c);
216         if (c>=' ' && c<0177) col++;
217     }
218 }
219 #endif
220
221 static
222 int a20(void)   /* Return 1 if a20 is enabled, 0 if disabled */
223 {
224 #asm
225  push   ds
226  push   es
227  xor    ax,ax
228  mov    es,ax
229  dec    ax
230  mov    ds,ax
231  cli
232  mov    al,[0x10]
233  mov    ah,al
234  seg es
235  cmp    al,[0]
236  jne    a20_8
237  xor    al,#0x5A
238  mov    [0x10],al
239  seg es
240  cmp    al,[0]
241  jne    a20_8
242  xor    al,al
243  jmp    a20_9
244 a20_8:
245  mov    al,#1
246 a20_9:
247  mov    [0x10],ah
248  cbw
249  sti
250  pop    es
251  pop    ds
252 #endasm
253 }
254
255 static
256 void sizeit(unsigned long sectors)
257 {
258 static char suf[] = "KMGT";
259     int fract;
260     char *cp;
261
262 /* print disk size in K,M,G,T */
263     sectors /= 2;
264     cp = suf;
265     if (sectors <= 999) {
266         printf("%ld%c", sectors, *cp);
267         return;
268     }
269     cp++;
270     while (sectors > 999999) {
271         sectors /= 1000;
272         cp++;
273     }
274     if (sectors > 2999) {
275         sectors *= 1024;
276         sectors /= 1000;
277     }
278     sectors += 5;       /* round decimal part */
279     sectors /= 10;
280     fract = sectors % 100;
281     sectors /= 100;
282     printf("%ld.%02d%c", sectors, fract, *cp);
283 }
284
285 static
286 void banner(char *version)
287 {
288         printf( "\n\n\n"
289 ">>>> Disk Detection and Parameter Display <<<<\n\n\n"
290 "Version %s, Copyright (C) 1999-2005  John Coffman <johninsd@san.rr.com>\n"
291 "Portions Copyright (C) 1996-2001 Robert de Bath, used with permission\n"
292 "Re-use and redistribution rights set forth in the file \"COPYING\".\n\n",
293          version);
294 }
295
296 static
297 void testDX(void)
298 {
299 #if __MSDOS__==0
300    printf("Boot reported from DX = 0x%04x (boot device is 0x%02x in DL)\n", __argr.x.dx, __argr.h.dl);
301    if (__argr.h.dl == 0 || __argr.h.dl == 1) {
302         printf("If you booted from the %s floppy drive, then this is correct.",
303                 __argr.h.dl ? "second" : "first");
304    } else if (__argr.h.dl >= 0x80 && __argr.h.dl <= 0x8f) {
305         printf("If you booted from %s hard drive, then this is correct.",
306                 __argr.h.dl==0x80 ? "the first" :
307                 __argr.h.dl==0x81 ? "the second" : "a" );
308    } else {
309         printf("It looks like the BIOS failed to report the boot device in DL.\n");
310    }
311 #endif
312 }
313
314
315
316 static
317 int smsw(void)
318 {
319 #asm
320   smsw  ax
321 #endasm
322 }
323
324 static
325 long e820(long b)
326 {
327 #asm
328   push  bp
329   mov   bp,sp
330   push  ds
331   pop   es
332   mov   di,#_mem_map
333   mov   eax,#0xE820
334   mov   ebx,[bp+4]
335   mov   ecx,#20
336   mov   edx,#E820_MAGIC
337   stc
338   int   0x15
339   jc    e820_err
340   cmp   eax,#E820_MAGIC
341   mov   ax,#-2
342   jne   e820_exit
343   cmp   ecx,#20
344   mov   ax,#-3
345   jne   e820_exit
346   push  ebx
347   pop   ax
348   pop   dx
349   jmp   e820_exit
350 e820_err:
351   mov   ax,#-1
352 e820_err2:
353   cwd
354 e820_exit:
355   leave
356 #endasm
357 }
358
359 static
360 int inb(int port)
361 {
362 #asm
363   mov   bx,sp
364   mov   dx,[bx+2]
365   in    al,dx
366   xor   ah,ah
367 #endasm
368 }
369
370 static
371 int outb(int port, int data)
372 {
373 #asm
374   mov   bx,sp
375   mov   dx,[bx+2]
376   mov   ax,[bx+4]
377   out   dx,al
378 #endasm
379 }
380
381 static
382 void v86test(void)
383 {
384 static char s1[] = "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n";
385
386     if (smsw()&1) {
387         printf(s1);
388         printf( "!!! ***  Warning:  DOS is not running in REAL mode  *** !!!\n"
389                 "!!! ***     Reported results may not be accurate    *** !!!\n" );
390         printf(s1);
391     }
392 }
393
394 static
395 void yesno(int i)
396 {
397     printf("%s\n", i?"yes":"no");
398 }
399
400 static
401 void decimal(unsigned long value)
402 {
403     unsigned int v[4];
404     int i;
405     for (i=0; i<4; i++) {
406         v[i] = value % 1000;
407         value /= 1000;
408     }
409     if (v[3]) printf("%d,%03d,%03d,%03d", v[3], v[2], v[1], v[0]);
410     else if (v[2]) printf("%d,%03d,%03d", v[2], v[1], v[0]);
411     else if (v[1]) printf("%d,%03d", v[1], v[0]);
412     else printf("%d", v[0]);
413 }
414
415 static
416 void print_regs(union REGS *reg) {
417     printf("AX=%04x  BX=%04x  CX=%04x  DX=%04x  SI=%04x  DI=%04x\n",
418     reg->x.ax, reg->x.bx, reg->x.cx, reg->x.dx, reg->x.si, reg->x.di);
419 }
420
421 static 
422 void print_sregs(struct SREGS *sreg) {
423     printf("DS=%04x  ES=%04x  CS=%04x  SS=%04x\n",
424                 sreg->ds, sreg->es, sreg->cs, sreg->ss);
425 }
426
427 static
428 int is_msdos(void)
429 {
430 #if __MSDOS__
431     return 1;
432 #else
433     return (__argseg.es+0x10 == __argseg.cs);
434 #endif
435 }
436
437 static
438 void pause(void)
439 {
440         char ch;
441 /* Must be standalone */
442         check_fs();
443         printf("\n\n\nHit <Enter> to continue, <^C> to quit ...");
444         do {
445             ch = getch();
446             if (ch==CTRL_C) exit(0);
447 #if DEBUG>=1
448             if (ch != CR) printf(" %o", ch);
449 #endif
450         } while (ch != CR);
451         printf("\n");
452 }
453
454 static
455 void video_fix(void)
456 {
457    outb(SEQ, 1);
458    video_1 = inb(SEQ+1);
459 /* dirty hack for DELL Dimension 4300 computers */
460    printf("\f\n");
461 }
462
463 static void print_carry(int flag)
464 {
465     printf("    Carry = %d\n", flag);
466 }
467
468 static
469 void get_equip_cfg(void)
470 {
471 static char *vmode[4] = { "reserved", "40x25 color",
472                 "80x25 color", "80x25 monochrome" };
473     word flag;
474     int n;
475     
476     pause();
477     printf(SPACER
478                 "Int 11h\t\t\t\t[PC][AT][PS/2]\n"
479                 "Get Equipment Configuration\n\n"
480                 "Returns:\n    ");
481     flag = int86(0x11, &reg, &reg);
482     print_regs(&reg);
483     
484     printf("\nHas floppy drive(s): "); yesno(flag&1);
485     printf("Has math coprocessor: "); yesno(flag&2);
486     printf("Has pointing device: "); yesno(flag&4);
487     printf("Initial video mode: %s\n", vmode[(flag>>4)&3]);
488     n = flag&1 ? ((flag>>6)&3)+1 : 0;
489     if (n) printf("Floppy drives installed: %d\n", ((flag>>6)&3)+1 );
490     n = (flag>>9)&7;
491     printf("Serial interfaces: %d\n", n);
492     printf("Has game adapter: "); yesno(flag&4096);
493     n = (flag>>14)&3;
494     printf("Parallel interfaces: %d\n", n);
495 }
496
497 static
498 void get_conv_mem(void)
499 {
500     int mem;
501     
502     pause();
503     printf(SPACER
504                 "Int 12h\t\t\t\t[PC][AT][PS/2]\n"
505                 "Get Conventional Memory Size\n\n"
506                 "Returns:\n    ");
507     mem = int86(0x12, &reg, &reg);
508     print_regs(&reg);
509     printf("\nThere is %dK of low memory.    EBDA size = %dK   EBDA starts at 0x%lx\n",
510         mem, 640-mem, (long)mem<<10 );
511     printf("\n(The A20 line is %sabled.)\n", a20() ? "en" : "dis");
512 }
513
514 static
515 void mov_ext_mem(void)
516 {
517     word status, temp, vtemp;
518     dword high, veryhigh;
519         
520     pause();
521     segread(&sreg);
522     sreg.es = sreg.ds;
523     printf(SPACER
524                 "Int 15h  Function 87h\t\t[AT][PS/2]\n"
525                 "Move Extended Memory Block\n\n"
526                 "Call With:\n    ");
527                 print_sregs(&sreg);
528                 reg.x.ax = 0x8700;
529                 reg.x.cx = 1;
530                 reg.x.si = &gdt;
531                 printf("    ");
532                 print_regs(&reg);
533                 
534         high = 1024L*(1024L+128)-2;     /* 1Mb + 128K */
535         veryhigh = high+16L*1024L*1024L;
536         if (veryhigh >= hma) veryhigh=0;
537 #define WORDA 0xA5C6
538 #define WORDB 0x6CA5
539
540         errno = 0;
541         temp = hipeekw(high);
542         status = hipokew(high, WORDA^temp);
543         printf("\nReturns:\n    ");
544         print_sregs(&sreg);
545         printf("    ");
546         print_regs(&oreg);
547         print_carry((status>>8) & 1);
548
549         printf("\nR/W test at address %08lx ", high);
550         if (hipeekw(high) != (WORDA^temp)) errno |= 0x400;
551         hipokew(high, temp);
552         if (hipeekw(high) != temp) errno |= 0x800;
553         printf("%ssuccessful\n", errno ? "un" : "");
554         if (errno) printf("Error code = 0x%04x\n", errno);
555         
556         if (veryhigh) {
557                 printf("R/W test at address %08lx ", veryhigh);
558                 vtemp = hipeekw(veryhigh);
559                 hipokew(veryhigh, WORDB^vtemp);
560                 if (hipeekw(high) != temp) errno |= 0x200;
561                 if (hipeekw(veryhigh) != (WORDB^vtemp)) errno |= 0x400;
562                 hipokew(veryhigh, vtemp);
563                 if (hipeekw(high) != temp) errno |= 0x200;
564                 if (hipeekw(veryhigh) != vtemp) errno |= 0x800;
565                 printf("%ssuccessful\n", errno ? "un" : "");
566                 if (errno) printf("Error code = 0x%04x\n", errno);
567         }
568         if (errno & 0xE00)
569             printf("\nThere is crosstalk between the two addresses\n"
570                 "The function does not support full 386 32-bit addressing.\n");
571 }
572
573 #define NAREA 32
574
575 static
576 void get_ext_mem(void)
577 {
578     long b, b1;
579     dword t;
580     int i;
581     
582     pause();
583     printf(SPACER
584                 "Int 15h  Function E820h\t\t[EXT]\n"
585                 "Get Memory Map\n\n"
586                 "Call With:\n"
587                 "    EAX=0000E820  EBX=00000000  ECX=00000014  EDX=%lx\n\n",
588                 E820_MAGIC );
589
590     b = e820(b1=i=0);
591     if (b > 0) {
592         dword start[NAREA], length[NAREA];
593         int j, k, ovlap;
594
595     /*           00    000000000000   000000000000   (1) avail  */
596         printf("EBX        Start         Length      Type\n\n");
597         do {
598             printf(" %02lx    %04hx%08lx   %04hx%08lx   (%d) %s\n", b1,
599                 (short)mem_map.start_hi, mem_map.start,
600                 (short)mem_map.length_hi, mem_map.length, (int)mem_map.mtype,
601                 mem_map.mtype == 1 ? "available" :
602                 mem_map.mtype == 2 ? "reserved"  :
603                 mem_map.mtype == 3 ? "ACPI" :
604                 mem_map.mtype == 4 ? "NVS"  :  "unknown-reserved");
605             if (mem_map.mtype==1 && mem_map.start_hi==0 && mem_map.start<=1024L*1024L) {
606                 if (mem_map.length_hi==0) hma = mem_map.start+mem_map.length;
607                 else hma = 0xFFF00000L;
608             }
609             if (i < NAREA) {
610                 start[i] = *(dword*)(((char*)&mem_map.start)+1);
611                 length[i] = *(dword*)(((char*)&mem_map.length)+1);
612             }
613             i++;
614             b = e820(b1=b);
615         } while (b1 > 0);
616         printf("\n");
617         if (i > NAREA) {
618             i = NAREA;
619         }
620         ovlap = 0;
621         for (k=0; k<i-1; k++) {
622             dword s, e;
623             s = start[k];
624             e = s + length[k];
625             for (j=k+1; j<i; j++) {
626                 dword ss, ee;
627                 ss = start[j];
628                 ee = ss + length[j];
629                 if (!(ss < s && ee <= s || ss >= e && ee > e)) {
630                     printf("*** Memory areas %d and %d overlap ***\n", k, j);
631                     ovlap++;
632                 }
633             }
634         }
635         if (!ovlap) printf("No memory areas overlap\n");
636     } else {
637         printf("Returns:\n");
638         if (b==-1) print_carry(1);
639         else if (b==-2) printf("    EAX=<trash>\n");
640         else if (b==-3) printf("    EAX=%lx  EBX=********  ECX=<trash>\n", E820_MAGIC);
641         else printf("    EAX=%lx  EBX=00000000\n", E820_MAGIC);
642         printf("\nFunction is not supported.\n");
643     }
644
645
646     pause();
647     printf(SPACER
648                 "Int 15h  Function E801h\t\t[EXT]\n"
649                 "Get Extended Memory Blocks\n\n"
650                 "Call With:\n    ");
651     reg.x.ax = 0xE801;
652     print_regs(&reg);
653     int86(0x15, &reg, &oreg);
654     printf("\nReturns:\n    ");
655     print_regs(&oreg);
656     print_carry(oreg.x.cflag);
657
658     if (!oreg.x.cflag) {
659         printf("\nNumber of 1K blocks between 1M and 16M:  %u\n", oreg.x.ax);
660         printf(  "Number of 64K blocks above 16M:  %u\n", oreg.x.bx);
661         t = 1024L*(oreg.x.ax+1024);
662         t += 64L*1024L*oreg.x.bx;
663         if (!hma) hma = t;
664         else if (hma!=t) printf("A different amount of memory is reported by this function\n");
665     }
666     else printf("\nFunction is not supported.\n");
667
668     pause();
669     printf(SPACER
670                 "Int 15h  Function 88h\t\t[AT][PS/2]\n"
671                 "Get Extended Memory Size\n\n"
672                 "Call With:\n    ");
673     reg.x.ax = 0x8800;
674     print_regs(&reg);
675     int86(0x15, &reg, &reg);
676     printf("\nReturns:\n    ");
677     print_regs(&reg);
678     printf("\nThere is ");
679     decimal( (unsigned long)reg.x.ax );
680     printf("K of extended memory.\n");
681
682     t = (reg.x.ax + 1024L) * 1024L;
683     if (!hma) hma = t;
684 }    
685     
686 static
687 int get_video_mode(void)
688 {
689     int m, row, col;
690     
691     pause();
692     printf(SPACER
693                 "Int 10h  Function 0Fh\t\t[MDA][CGA][PCjr]\n"
694                 "Get Video Mode\t\t\t[EGA][MCGA][VGA]\n\n"
695                 "Call With:\n    ");
696     reg.x.ax = 0x0F00;
697     reg.x.bx = -1;
698     print_regs(&reg);
699     int86(0x10, &reg, &reg);
700     printf("\nReturns:\n    ");
701     print_regs(&reg);
702     m = reg.h.al;
703     __set_es(0x40);             /* address BIOS data area */
704     reg.h.bl = row = __peek_es(0x84);
705     printf("Fetch 0040:0084 (rows-1) to BL\n"
706         "--> ");
707     print_regs(&reg);
708     col = reg.h.ah;
709     printf("\nVideo mode = 0x%02x (%dx%d %s)\n", m, col, row+1,
710         m==7 ? "monochrome" : m==3 ? "color" : "unknown");
711     printf("Active display page = %d\n", (int)reg.h.bh);
712     
713     return !(m==7 || col<80);
714 }
715
716 static
717 int get_cfg_info(void)
718 {
719     pause();
720     printf(SPACER
721                 "Int 10h  Function 12h\t\t[EGA][VGA]\n"
722                 "Subfunction 10h\n"
723                 "Get Configuration Information\n\n"
724                 "Call With:\n    ");
725     reg.x.ax = 0x1200;
726     reg.x.bx = 0xFF10;
727     print_regs(&reg);
728     int86(0x10, &reg, &reg);
729     printf("\nReturns:\n    ");
730     print_regs(&reg);
731     if (reg.h.bh > 1) return 0;
732     
733     printf("\n%s display\n", reg.h.bh==0 ? "Color" : reg.h.bh==1 ? "Monochrome" : "Unknown");
734     printf("EGA memory = %dK\n", reg.h.bl <= 3 ? (reg.h.bl+1)*64 : 0);
735     printf("Feature bits = 0x%02x\n", (int)reg.h.ch);
736     printf("Configuration switch = 0x%02x\n", (int)reg.h.cl);
737     
738     return 1;
739 }
740
741 static
742 int enable_refresh(void)
743 {
744     pause();
745     printf(SPACER
746                 "Int 10h  Function 12h\t\t[VGA]\n"
747                 "Subfunction 36h\n"
748                 "Enable Screen Refresh\n\n"
749                 "Call With:\n    ");
750     reg.x.ax = 0x1200;
751     reg.x.bx = 0x0036;
752     reg.x.cx = 0;
753     reg.x.dx = 0x80;
754     print_regs(&reg);
755     int86(0x10, &reg, &oreg);
756     printf("\nReturns:\n    ");
757     print_regs(&oreg);
758     
759     printf("\n");
760     printf("Function is %ssupported.\n", oreg.h.al==0x12 ? "" : "NOT ");
761     if (oreg.x.dx != reg.x.dx || oreg.x.cx != reg.x.cx || oreg.x.si != reg.x.si
762                 || oreg.x.di != reg.x.di)
763         printf("Error: Register(s) are not preserved.\n");
764     reg.x.dx = 0;
765     
766     return 1;
767 }
768
769 static
770 int get_comb_code(void)
771 {
772 static char *dcode[] = { "none", "Monochrome", "CGA", "reserved",
773   "EGA (color)", "EGA (mono)", "PGA", "VGA (monochrome)", "VGA (color)",
774   "reserved", "MCGA (digital color)", "MCGA (monochrome)", "MCGA (color)",
775   "UNKNOWN" };
776     int code;
777     
778     pause();
779     printf(SPACER
780                 "Int 10h  Function 1Ah\t\t[PS/2]\n"
781                 "Subfunction 00h\n"
782                 "Get Display Combination Code\n\n"
783                 "Call With:\n    ");
784     reg.x.ax = 0x1A00;
785     reg.x.bx = reg.x.cx = 0;
786     print_regs(&reg);
787     int86(0x10, &reg, &reg);
788     printf("\nReturns:\n    ");
789     print_regs(&reg);
790     if (reg.h.al != 0x1A) return 0;
791     
792     code = reg.h.bl <= 12 ? reg.h.bl : 13;
793     printf("\nActive display: %s\n", dcode[code]);
794     code = reg.h.bh <= 12 ? reg.h.bh : 13;
795     printf("Inactive display: %s\n", dcode[code]);
796
797     return (reg.h.bl>=4);
798 }
799
800 static void print_io_status(int status)
801 {
802 static char *errmsg[] = {"no error", "invalid command",                 /* 0-1 */
803         "address mark not found", "disk write-protected",       /* 2-3 */
804         "sector not found", "reset failed", "floppy disk removed", /* 4-6 */
805         "bad parameter table", "DMA overrun",                   /* 7-8 */
806         "DMA crossed 64k boundary", "bad sector flag",          /* 9-A */
807         "bad track flag", "media type not found",               /* B-C */
808         "invalid number of sectors on format",                  /* D */
809         "control data address mark detected",                   /* E */
810         "DMA arbitration level out of range",                   /* F */
811         "uncorrectable CRC or ECC data error",                  /* 10 */
812         "ECC corrected data error"                              /* 11 */
813                         };
814     char *err;
815                         
816     if (status <= 0x11) err = errmsg[status];
817     else switch(status) {
818         case 0x20: err = "controller failure"; break;
819         case 0x40: err = "seek failed"; break;
820         case 0x80: err = "disk timeout (failed to respond)"; break;
821         case 0xAA: err = "drive not ready"; break;
822         case 0xBB: err = "undefined error"; break;
823         case 0xCC: err = "write fault"; break;
824         case 0xE0: err = "status register error"; break;
825         case 0xFF: err = "sense operation failed"; break;
826         default:   err = "???";
827     }
828     printf("    BIOS error code = 0x%02x  (%s)\n", status, err);
829 }
830
831 static
832 void do_edd(int dev)
833 {
834     int m, subset;
835     
836     pause();
837     printf(SPACER
838                 "Int 13h  Function 41h\t\t[EDD]\n"
839                 "Check EDD Extensions Present (device %02xh)\n\n"
840                 "Call With:\n    ", dev);
841     reg.x.ax = 0x41ED;
842     reg.x.bx = 0x55AA;
843     reg.x.dx = dev;
844     print_regs(&reg);
845     int86(0x13, &reg, &oreg);
846     printf("\nReturns:\n    ");
847     print_regs(&oreg);
848     print_carry(oreg.x.cflag);
849     m = 0;
850     if (oreg.x.cflag) print_io_status(oreg.h.ah);
851     else if (oreg.x.bx == 0xAA55 && (oreg.x.cx&EDD_SUBSET+EDD_LOCK+EDD_PACKET)) {
852         m = 1;
853         printf("\nEnhanced Disk Drive support: "); yesno(subset=oreg.x.cx&EDD_SUBSET);
854         printf("Drive locking and ejecting: "); yesno(oreg.x.cx&EDD_LOCK);
855         printf("Device access using packet calls: "); yesno(oreg.x.cx&EDD_PACKET);
856         printf("EDD extensions version%s (hex code %02xh)\n",
857                 oreg.h.ah==0x30 ? " 3.0" : oreg.h.ah==0x21 ? " 1.1" : "" ,oreg.h.ah);
858     }
859     
860     if (m) {
861         struct EDDparam {
862             short size;         /* size of this structure */
863             short flags;        /* information flags */
864             long pcyls;         /* number of physical cylinders */
865             long pheads;        /* number of physical heads/cylinder */
866             long psects;        /* number of physical sectors/track */
867             unsigned            /* number of physical sectors on volume */
868             long sectors_lo, sectors_hi;        /* this is 8 bytes long */
869             short sec_size;     /* number of bytes per sector */
870             unsigned
871             long params;        /* EDD config params (valid only if EDD_SUBSET) */
872         } eddparam;
873
874         pause();
875         m = !!(oreg.x.cx&EDD_SUBSET)  &&  oreg.h.ah>=0x21;
876         printf(SPACER
877                 "Int 13h  Function 48h\t\t[EDD]\n"
878                 "EDD Get Drive Parameters (device %02xh)\n\n"
879                 "Call With:\n    ", dev);
880         eddparam.size = sizeof(eddparam);
881         reg.x.si = &eddparam;   /* DS:SI points to buffer */
882         reg.x.ax =0x48C6;
883         segread(&sreg);
884         print_sregs(&sreg);
885         printf("    ");
886         print_regs(&reg);
887         int86x(0x13, &reg, &reg, &sreg);
888         printf("\nReturns:\n    ");
889         print_sregs(&sreg);
890         printf("    ");
891         print_regs(&reg);
892         print_carry(reg.x.cflag);
893 #define fl eddparam.flags       
894         printf("\nDMA boundary errors handled transparently: "); yesno(fl&1);
895         printf("Geometry supplied: "); yesno(fl&2);
896         printf("Device is removable: "); yesno(fl&4);
897         printf("Device supports write with verify: "); yesno(fl&8);
898         if (fl&4) {
899             printf("Device has change-line support: "); yesno(fl&16);
900             printf("Device is lockable: "); yesno(fl&32);
901             printf("No media present; geometry is set to maximum: "); yesno(fl&64);
902         }
903         printf("Disk geometry (");
904         if (fl&2) {
905             printf("C:H:S) = %ld:%ld:%ld (", eddparam.pcyls, eddparam.pheads,
906                                 eddparam.psects);
907         }
908         if (eddparam.sectors_hi == 0) 
909             decimal(eddparam.sectors_lo);
910         else printf("0x%x%08x", eddparam.sectors_hi, eddparam.sectors_lo);
911         printf(" sectors)\n");
912 #undef fl
913         m=1;
914         if (m) {
915             static char *cfunc[] = {
916                 "Enable Prefetch",
917                 "Disable Prefetch",
918                 "Set Maximum PIO Mode",
919                 "Set PIO Mode 0",
920                 "Set Default PIO Mode",
921                 "Enable DMA Maximum Mode",
922                 "Disable DMA"
923             };
924             m = 0;              /* start with subfn 0 */
925             pause();
926             printf(SPACER
927                 "Int 13h  Function 4Eh\t\t[EDD]\n"
928                 "Subfunction 0?h\n"
929                 "EDD Set Hardware Configuration (device %02xh)\n\n"
930                 "Call With:\n    ", dev);
931             reg.x.ax = 0x4E00;
932             reg.h.dl = dev;
933             print_regs(&reg);
934             int86(0x13, &reg, &reg);
935             printf("\nReturns:\n    ");
936             print_regs(&reg);
937             print_carry(reg.x.cflag);
938             printf("\n");
939             
940             for (m=0; m<nelem(cfunc); m++) {
941                 reg.x.ax = 0x4E00 + m;
942                 reg.h.dl = dev;
943                 int86(0x13, &reg, &reg);
944                 printf("Subfn(%d):  %s  <--  ", m, cfunc[m]);
945                 if (reg.x.cflag || reg.h.ah) {
946                     printf("is not supported.\n");
947                 }
948                 else {
949                     printf("%s other drives on controller.\n",
950                         reg.h.al ? "affects" : "does not affect");
951                 }
952             } /* for */
953             
954         } /* if (m) */
955         
956     } /* if (m) */
957 }
958
959 static
960 int do_disk(int dev)
961 {
962 static char *drvtyp[] = {"No drive present", "Floppy w/o change-line support",
963         "Floppy with change-line support"};
964 static char *dt[] = {   "5.25\", 40 track, 360K",
965                         "5.25\", 80 track, 1.2M",
966                         "3.5\", 80 track, 720K",
967                         "3.5\", 80 track, 1.44M" };
968     int m, mm;
969     int c,h,s;
970     unsigned long sect;
971     
972     pause();
973     printf(SPACER
974                 "Int 13h  Function 15h\t\t[AT][PS/2]\n"
975                 "Get Disk Type  (device %02xh)\n\n"
976                 "Call With:\n    ", dev);
977     reg.x.ax = 0x1500;
978     reg.x.bx = 0;
979     reg.x.dx = dev;
980     print_regs(&reg);
981     int86(0x13, &reg, &oreg);
982     printf("\nReturns:\n    ");
983     print_regs(&oreg);
984     print_carry(oreg.x.cflag);
985     mm = (oreg.x.cflag==0 && oreg.h.ah!=0);
986     m = mm || (dev&0x80)==0;
987     if (oreg.x.cflag) print_io_status((int)oreg.h.ah);
988     else {
989         printf("\n%s",
990             oreg.h.ah < 3 ? drvtyp[oreg.h.ah] :
991             oreg.h.ah != 3 ? "unknown drive type" : "");
992         if (oreg.h.ah == 3) {
993             printf("Fixed disk with ");
994             sect = (long)oreg.x.cx<<16 | oreg.x.dx;
995             decimal(sect);
996             printf(" sectors = ");
997             sizeit(sect);
998         }
999         printf("\n");
1000     }
1001
1002     if (m) {
1003         pause();
1004         printf(SPACER
1005                         "Int 13h  Function 08h\t\t[PC][AT][PS/2]\n"
1006                         "Get Drive Parameters  (device %02xh)\n\n"
1007                         "Call With:\n    ", dev);
1008         reg.x.ax = 0x0800;
1009         reg.x.dx = dev;
1010         reg.x.di = 0x4321;
1011         reg.x.bx = 0x1234;
1012         segread(&sreg);
1013         sreg.es = 0;
1014         print_sregs(&sreg);
1015         printf("    ");
1016         print_regs(&reg);
1017         int86x(0x13, &reg, &oreg, &sreg);
1018         printf("\nReturns:\n    ");
1019         print_sregs(&sreg);
1020         printf("    ");
1021         print_regs(&oreg);
1022         print_carry(oreg.x.cflag);
1023         if (oreg.x.cflag) print_io_status((int)oreg.h.ah);
1024         else {
1025             last_good_disk = dev;
1026             if (mm) {
1027                 printf("\n");
1028                 if (!(dev&0x80)) {
1029                     printf("Disk type %d = %s\n", (int)oreg.h.bl,
1030                                         dt[(oreg.h.bl-1)&3] );
1031                     printf("Parameter table at %04x:%04x\n",
1032                                         sreg.es, oreg.x.di);
1033                 }
1034                 else {
1035                     if (oreg.x.bx != reg.x.bx)
1036                         printf("Error:  Hard disk BIOS should not touch BX\n");
1037                     if (sreg.es != 0  ||  oreg.x.di != reg.x.di)
1038                         printf("Error:  Hard disk BIOS should not touch ES:DI\n");
1039                 }
1040                 s = (oreg.h.cl & 0x3F);         /* sectors 1..63 */
1041                 if (s == 0) s = 64;
1042                 h = (int)oreg.h.dh + 1;         /* heads 0..254 */
1043                 c = (((oreg.h.cl & 0xC0)<<2) | oreg.h.ch) + 1;
1044                 printf("Disk geometry (C:H:S) = %d:%d:%d (", c, h, s);
1045                 decimal( sect=(long)c*h*s );
1046                 printf(" sectors) = ");
1047                 sizeit(sect);
1048                 printf("\n%s disks on system = %d\n",
1049                         dev&0x80 ? "Fixed" : "Floppy", oreg.h.dl);
1050                 if (s > 63)  printf("BIOS BUG!!!  sectors returned as zero; 64 assumed\n");
1051                 if (h > 255) printf("BIOS BUG!!!  heads > 255; BIOS is not IBM compatible\n");
1052                 if (dev & 0x80) {
1053                     if (dev == 0x80) num_hd = oreg.h.dl;
1054                     do_edd(dev);
1055                 }
1056             }
1057         }
1058     }
1059     
1060     return m;
1061 }
1062
1063 static
1064 int do_rw(int tries)
1065 {
1066     int code;
1067     
1068     while (tries--) {
1069         int86x(0x13, &reg, &oreg, &sreg);
1070         if (oreg.x.cflag == 0 && oreg.x.ax == 0x0001) return 0;
1071         code = oreg.h.ah;
1072         oreg.x.ax = 0;
1073         int86(0x13, &oreg, &oreg);
1074     }
1075     return code;
1076 }
1077
1078 static
1079 void do_get_pt(int dev)
1080 {
1081     int m;
1082     char buf[SECTOR_SIZE];
1083     
1084     printf("Get partition table (device = 0x%x): ", dev);
1085     segread(&sreg);
1086     sreg.es = sreg.ss;
1087     reg.x.ax = 0x0201;
1088     reg.x.bx = buf;
1089     reg.x.cx = 1;
1090     reg.x.dx = dev & 0xFF;
1091     m = do_rw(5);
1092     if (m) print_io_status(m);
1093     else printf("  okay");
1094     printf("\n");
1095     check_fs();
1096 }
1097
1098 static
1099 void do_vesa(void)
1100 {
1101     int i;
1102     char vesa[512];
1103     
1104     pause();
1105     printf(SPACER
1106                 "Int 10h  Function 4Fh\t\t[VESA]\n"
1107                 "Subfunction 00h\n"
1108                 "Check VESA Extensions Present\n\n"
1109                 "Call With:\n    ");
1110     reg.x.ax = 0x4F00;
1111     reg.x.bx = 0;
1112     segread(&sreg);
1113     sreg.es = sreg.ss;
1114     reg.x.di = &vesa[0];
1115     print_sregs(&sreg);
1116     printf("    ");
1117     print_regs(&reg);
1118     int86x(0x10, &reg, &oreg, &sreg);
1119     printf("\nReturns:\n    ");
1120     print_sregs(&sreg);
1121     printf("    ");
1122     print_regs(&oreg);
1123     if (oreg.x.ax != 0x004F) {
1124         printf("\nVESA BIOS extensions not present\n");
1125         return;
1126     }
1127     if (strncmp(vesa, "VESA", 4)) {
1128         printf("\nVESA signature not found\n");
1129         return;
1130     }
1131     vesa[4] = 0;
1132     printf("\n\"%s\" BIOS extensions present\n", vesa);
1133
1134     pause();
1135     printf(SPACER
1136                 "Int 10h  Function 4Fh\t\t[VESA]\n"
1137                 "Subfunction 01h\n"
1138                 "Get VESA Mode Information 1\n\n"
1139                 "Call With:\n    ");
1140     reg.x.ax = 0x4F01;
1141     reg.x.cx = 0x101;
1142     segread(&sreg);
1143     sreg.es = sreg.ss;
1144     reg.x.di = &vesa[0];
1145     print_sregs(&sreg);
1146     printf("    ");
1147     print_regs(&reg);
1148     int86x(0x10, &reg, &oreg, &sreg);
1149     printf("\nReturns:\n    ");
1150     print_sregs(&sreg);
1151     printf("    ");
1152     print_regs(&oreg);
1153     i = *(int*)vesa;    /* get mode bits */
1154     printf("\nMode bits:  0x%04x\n", i);
1155     printf("640x480x256 mode supported: ");
1156     yesno(!(0x19 & ~i));
1157     
1158     pause();
1159     printf(SPACER
1160                 "Int 10h  Function 4Fh\t\t[VESA]\n"
1161                 "Subfunction 01h\n"
1162                 "Get VESA Mode Information 3\n\n"
1163                 "Call With:\n    ");
1164     reg.x.ax = 0x4F01;
1165     reg.x.cx = 0x103;
1166     segread(&sreg);
1167     sreg.es = sreg.ss;
1168     reg.x.di = &vesa[0];
1169     print_sregs(&sreg);
1170     printf("    ");
1171     print_regs(&reg);
1172     int86x(0x10, &reg, &oreg, &sreg);
1173     printf("\nReturns:\n    ");
1174     print_sregs(&sreg);
1175     printf("    ");
1176     print_regs(&oreg);
1177     i = *(int*)vesa;    /* get mode bits */
1178     printf("\nMode bits:  0x%04x\n", i);
1179     printf("800x600x256 mode supported: ");
1180     yesno(!(0x19 & ~i));
1181 }
1182
1183 void main(void)
1184 {
1185     int m, i, dev;
1186     
1187     set_fs(FS_REG);
1188 #if DEBUG>=1
1189     printf("FS=%04x\n", get_fs());
1190     pause();
1191 #endif
1192     
1193     if (!is_msdos()) {
1194 /**     atexit(pause);  **/
1195         video_fix();    /* for Dumb DELL computers */
1196     }
1197 #if DEBUG>=1 && __MSDOS__==0
1198     printf("Beginning of '___cstartup'\n");
1199     print_regs(&__argr);
1200     printf("DS=%04x  ES=%04x  CS=%04x  SS=%04x  SP=%04x  BP=%04x\n",
1201         __argseg.ds, __argseg.es, __argseg.cs, __argseg.ss,
1202         __argr.x.flags, __argr.x.cflag);
1203         
1204     segread(&sreg);
1205     printf("\nBeginning of '_main'\n");
1206     check_fs();
1207     print_sregs(&sreg);
1208 #endif
1209     check_fs();
1210     banner(DISK_VERSION);
1211     check_fs();
1212     v86test();
1213     testDX();
1214 #if DEBUG>=2
1215     sizeit((long)40*2*9);  putch('\n');
1216     sizeit((long)80*2*15);  putch('\n');
1217     sizeit((long)80*2*18);  putch('\n');
1218     sizeit((long)80*2*36);  putch('\n');
1219     sizeit((long)1024*255*63);  putch('\n');
1220     sizeit((long)24000*512);  putch('\n');
1221 #endif
1222     get_equip_cfg();
1223     get_conv_mem();
1224     hma = 0;
1225     get_ext_mem();
1226     if (hma>1024L*1024L) mov_ext_mem();
1227     m = get_video_mode();
1228     if (m) m = get_cfg_info();
1229     if (m) m = enable_refresh();
1230     if (m) m = get_comb_code();
1231     if (m) do_vesa();
1232 #if DEBUG>=3
1233     printf("\n\nm=%x\n", m);
1234 #endif
1235     dev = 0; m = 1;
1236     for (i=BD_MAX_FLOPPY; i && m;) {
1237         m = do_disk(dev);
1238         ++dev;
1239         if (--i == 0  &&  (dev & 0x80)==0) {
1240             dev = 0x80;
1241             i = BD_MAX_HARD;
1242         }
1243         if ((dev & 0x7F) >= num_hd) m = 0;
1244     }
1245     pause();
1246     printf(SPACER);
1247     for (dev = 0x80; dev <= last_good_disk; dev++) do_get_pt(dev);
1248     
1249     if (!is_msdos()) {
1250         printf("\n\nInitial SEQ reg 1:  0x%02x\n", video_1);
1251     }
1252     printf("The FS register was %smodified during the tests.\n",
1253             fs_mod ? "" : "NOT ");
1254     pause();
1255 }