]> 4ch.mooo.com Git - 16.git/blob - src/lib/doslib/hw/isapnp/isapnp.c
added a bunch of things~ and midi stuff~
[16.git] / src / lib / doslib / hw / isapnp / isapnp.c
1
2 #include <stdio.h>
3 #include <conio.h> /* this is where Open Watcom hides the outp() etc. functions */
4 #include <stdlib.h>
5 #include <string.h>
6 #include <unistd.h>
7 #include <malloc.h>
8 #include <fcntl.h>
9 #include <dos.h>
10
11 #include <dos/dos.h>
12 #include <isapnp/isapnp.h>
13
14 #include <hw/8254/8254.h>               /* 8254 timer */
15
16 uint16_t                                isapnp_read_data = 0;
17 uint8_t                                 isapnp_probe_next_csn = 0;
18
19 const unsigned char isa_pnp_init_keystring[32] = {
20         0x6A,0xB5,0xDA,0xED,0xF6,0xFB,0x7D,0xBE,
21         0xDF,0x6F,0x37,0x1B,0x0D,0x86,0xC3,0x61,
22         0xB0,0x58,0x2C,0x16,0x8B,0x45,0xA2,0xD1,
23         0xE8,0x74,0x3A,0x9D,0xCE,0xE7,0x73,0x39
24 };
25
26 const char *isapnp_config_block_str[3] = {
27         "Allocated",
28         "Possible",
29         "Compatible"
30 };
31
32 const char *isa_pnp_devd_pnp_0x00xx[] = {       /* PNP00xx */
33         "AT interrupt controller",              /*      00 */
34         "EISA interrupt controller",            /*      01 */
35         "MCA interrupt controller",             /*      02 */
36         "APIC",                                 /*      03 */
37         "Cyrix SLiC MP interrupt controller"    /*      04 */
38 };
39
40 const char *isa_pnp_devd_pnp_0x01xx[] = {       /* PNP01xx */
41         "AT Timer",                             /*      00 */
42         "EISA Timer",                           /*      01 */
43         "MCA Timer"                             /*      02 */
44 };
45
46 const char *isa_pnp_devd_pnp_0x02xx[] = {       /* PNP02xx */
47         "AT DMA Controller",                    /*      00 */
48         "EISA DMA Controller",                  /*      01 */
49         "MCA DMA Controller"                    /*      02 */
50 };
51
52 const char *isa_pnp_devd_pnp_0x03xx[] = {                               /* PNP03xx */
53         "IBM PC/XT keyboard controller (83-key)",                       /*      00 */
54         "IBM PC/AT keyboard controller (86-key)",                       /*      01 */
55         "IBM PC/XT keyboard controller (84-key)",                       /*      02 */
56         "IBM Enhanced (101/102-key, PS/2 mouse support)",               /*      03 */
57         "Olivetti Keyboard (83-key)",                                   /*      04 */
58         "Olivetti Keyboard (102-key)",                                  /*      05 */
59         "Olivetti Keyboard (86-key)",                                   /*      06 */
60         "Microsoft Windows(R) Keyboard",                                /*      07 */
61         "General Input Device Emulation Interface (GIDEI) legacy",      /*      08 */
62         "Olivetti Keyboard (A101/102 key)",                             /*      09 */
63         "AT&T 302 keyboard",                                            /*      0A */
64         "Reserved by Microsoft",                                        /*      0B */
65         NULL,                                                           /*      0C */
66         NULL,                                                           /*      0D */
67         NULL,                                                           /*      0E */
68         NULL,                                                           /*      0F */
69         NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,                        /*      10-17 */
70         NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,                        /*      18-1F */
71         "Japanese 106-key keyboard A01",                                /*      20 */
72         "Japanese 101-key keyboard",                                    /*      21 */
73         "Japanese AX keyboard",                                         /*      22 */
74         "Japanese 106-key keyboard 002/003",                            /*      23 */
75         "Japanese 106-key keyboard 001",                                /*      24 */
76         "Japanese Toshiba Desktop keyboard",                            /*      25 */
77         "Japanese Toshiba Laptop keyboard",                             /*      26 */
78         "Japanese Toshiba Notebook keyboard",                           /*      27 */
79         NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,                        /*      28-2F */
80         NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,                        /*      30-37 */
81         NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,                        /*      38-3F */
82         "Korean 84-key keyboard",                                       /*      40 */
83         "Korean 86-key keyboard",                                       /*      41 */
84         "Korean Enhanced keyboard",                                     /*      42 */
85         "Korean Enhanced keyboard 101c",                                /*      43 */
86         "Korean Enhanced keyboard 103"                                  /*      44 */
87 };
88
89 const char *isa_pnp_devd_pnp_0x04xx[] = {       /* PNP04xx */
90         "Standard LPT printer port",            /*      00 */
91         "ECP printer port"                      /*      01 */
92 };
93
94 const char *isa_pnp_devd_pnp_0x05xx[] = {       /* PNP05xx */
95         "Standard PC COM port",                 /*      00 */
96         "16550A-compatible COM port",           /*      01 */
97         "Multiport serial device",              /*      02 */
98         NULL,                                   /*      03 */
99         NULL,                                   /*      04 */
100         NULL,                                   /*      05 */
101         NULL,                                   /*      06 */
102         NULL,                                   /*      07 */
103         NULL,                                   /*      08 */
104         NULL,                                   /*      09 */
105         NULL,                                   /*      0A */
106         NULL,                                   /*      0B */
107         NULL,                                   /*      0C */
108         NULL,                                   /*      0D */
109         NULL,                                   /*      0E */
110         NULL,                                   /*      0F */
111         "Generic IRDA-compatible",              /*      10 */
112         "Generic IRDA-compatible"               /*      11 */
113 };
114
115 const char *isa_pnp_devd_pnp_0x06xx[] = {       /* PNP06xx */
116         "Generic ESDI/IDE/ATA controller",      /*      00 */
117         "Plus Hardcard II",                     /*      01 */
118         "Plus Hardcard IIXL/EZ",                /*      02 */
119         "Generic IDE, Microsoft Device Bay Specification"/* 03 */
120 };
121
122 const char *isa_pnp_devd_pnp_0x07xx[] = {                       /* PNP07xx */
123         "PC standard floppy disk controller",                   /*      00 */
124         "Standard floppy, Microsoft Device Bay Specification"   /*      01 */
125 };
126
127 const char *isa_pnp_devd_pnp_0x09xx[] = {                       /* PNP09xx */
128         "VGA compatible",                                       /*      00 */
129         "Video Seven VRAM/VRAM II/1024i",                       /*      01 */
130         "8514/A compatible",                                    /*      02 */
131         "Trident VGA",                                          /*      03 */
132         "Cirrus Logic Laptop VGA",                              /*      04 */
133         "Cirrus Logic VGA",                                     /*      05 */
134         "Tseng ET4000",                                         /*      06 */
135         "Western Digital VGA",                                  /*      07 */
136         "Western Digital Laptop VGA",                           /*      08 */
137         "S3 911/924",                                           /*      09 */
138         "ATI Ultra Pro/Plus Mach32",                            /*      0A */
139         "ATI Ultra Mach8",                                      /*      0B */
140         "XGA compatible",                                       /*      0C */
141         "ATI VGA wonder",                                       /*      0D */
142         "Weitek P9000 graphics",                                /*      0E */
143         "Oak VGA",                                              /*      0F */
144
145         "Compaq QVision",                                       /*      10 */
146         "XGA/2",                                                /*      11 */
147         "Tseng W32/W32i/W32p",                                  /*      12 */
148         "S3 801/928/964",                                       /*      13 */
149         "Cirrus 5429/5434",                                     /*      14 */
150         "Compaq advanced VGA",                                  /*      15 */
151         "ATI Ultra Pro Turbo Mach64",                           /*      16 */
152         "Microsoft Reserved",                                   /*      17 */
153         "Matrox",                                               /*      18 */
154         "Compaq QVision 2000",                                  /*      19 */
155         "Tseng W128",                                           /*      1A */
156         NULL,NULL,NULL,NULL,NULL,                               /*      1B-1F */
157
158         NULL,NULL,NULL,NULL, NULL,NULL,NULL,NULL,               /*      20-27 */
159         NULL,NULL,NULL,NULL, NULL,NULL,NULL,NULL,               /*      28-2F */
160
161         "Chips & Technologies SVGA",                            /*      30 */
162         "Chips & Technologies accelerator",                     /*      31 */
163         NULL,NULL, NULL,NULL,NULL,NULL,                         /*      32-37 */
164         NULL,NULL,NULL,NULL, NULL,NULL,NULL,NULL,               /*      38-3F */
165
166         "NCR 77c22e SVGA",                                      /*      40 */
167         "NCR 77c32blt"                                          /*      41 */
168 };
169
170 const char *isa_pnp_devd_pnp_0x0Cxx[] = {                       /* PNP0Cxx */
171         "Plug & Play BIOS",                                     /*      00 */
172         "System board",                                         /*      01 */
173         "Motherboard resources",                                /*      02 */
174         "Plug & Play BIOS event interrupt",                     /*      03 */
175         "Math coprocessor",                                     /*      04 */
176         "APM BIOS",                                             /*      05 */
177         "(early PnP #06)",                                      /*      06 */
178         "(early PnP #07)",                                      /*      07 */
179         "ACPI system board",                                    /*      08 */
180         "ACPI embedded controller",                             /*      09 */
181         "ACPI control method battery",                          /*      0A */
182         "ACPI fan",                                             /*      0B */
183         "ACPI power button",                                    /*      0C */
184         "ACPI lid",                                             /*      0D */
185         "ACPI sleep button",                                    /*      0E */
186         "PCI interrupt link",                                   /*      0F */
187         "ACPI system indicator",                                /*      10 */
188         "ACPI thermal zone",                                    /*      11 */
189         "Device bay controller",                                /*      12 */
190         "Plug & Play BIOS (non-ACPI?)"                          /*      13 */
191 };
192
193 const char *isa_pnp_devd_pnp_0x0Axx[] = {                       /* PNP0Axx */
194         "ISA bus",                                              /*      00 */
195         "EISA bus",                                             /*      01 */
196         "MCA bus",                                              /*      02 */
197         "PCI bus",                                              /*      03 */
198         "VESA bus",                                             /*      04 */
199         "Generic ACPI bus",                                     /*      05 that's a bus? */
200         "Generic ACPI extended IO bus"                          /*      06 */
201 };
202
203 const char *isa_pnp_devd_pnp_0x0Exx[] = {                       /* PNP0Exx */
204         "Intel 82365-compatible PCMCIA controller",             /*      00 */
205         "Cirrus Logic CL-PD6720 PCMCIA controller",             /*      01 */
206         "VLSI VL82C146 PCMCIA controller",                      /*      02 */
207         "Intel 82365-compatible CardBus controller"             /*      03 */
208 };
209
210 const char *isa_pnp_devd_pnp_0x0Fxx[] = {                       /* PNP0Fxx */
211         "Microsoft bus mouse",                                  /*      00 */
212         "Microsoft serial",                                     /*      01 */
213         "Microsoft InPort",                                     /*      02 */
214         "Microsoft PS/2",                                       /*      03 */
215         "Mouse systems",                                        /*      04 */
216         "Mouse systems 3-button (COM2)",                        /*      05 */
217         "Genius mouse (COM1)",                                  /*      06 */
218         "Genius mouse (COM2)",                                  /*      07 */
219         "Logitech serial",                                      /*      08 */
220         "Microsoft BallPoint serial",                           /*      09 */
221         "Microsoft plug & play",                                /*      0A */
222         "Microsoft plug & play BallPoint",                      /*      0B */
223         "Microsoft compatible serial",                          /*      0C */
224         "Microsoft compatible InPort compatible",               /*      0D */
225         "Microsoft compatible PS/2",                            /*      0E */
226         "Microsoft compatible serial BallPoint compatible",     /*      0F */
227         "Texas Instruments QuickPort",                          /*      10 */
228         "Microsoft compatible bus mouse",                       /*      11 */
229         "Logitech PS/2",                                        /*      12 */
230         "PS/2 port for PS/2 mice",                              /*      13 */
231         "Microsoft kids mouse",                                 /*      14 */
232         "Logitech bus mouse",                                   /*      15 */
233         "Logitech swift device",                                /*      16 */
234         "Logitech compatible serial",                           /*      17 */
235         "Logitech compatible bus mouse",                        /*      18 */
236         "Logitech compatible PS/2",                             /*      19 */
237         "Logitech compatible swift",                            /*      1A */
238         "HP Omnibook mouse",                                    /*      1B */
239         "Compaq LTE trackball PS/2",                            /*      1C */
240         "Compaq LTE trackball serial",                          /*      1D */
241         "Microsoft kids trackball",                             /*      1E */
242         NULL                                                    /*      1F */
243 };
244
245 const char *isapnp_fml32_miosize_str[4] = {
246         "8-bit",
247         "16-bit",
248         "8/16-bit",
249         "32-bit"
250 };
251
252 const char *isapnp_sdf_priority_strs[3] = {
253         "Good",
254         "Acceptable",
255         "Sub-optimal"
256 };
257
258 const char *isapnp_dma_speed_str[4] = {
259         "Compatible",
260         "Type A",
261         "Type B",
262         "Type F"
263 };
264
265 const char *isapnp_dma_xfer_preference_str[4] = {
266         "8-bit",
267         "8/16-bit",
268         "16-bit",
269         "??"
270 };
271
272 const char *isapnp_tag_strs[] = {
273         /* small tags +0x00 */
274         NULL,                                   /* 0x00 */
275         "PnP version",
276         "Logical device ID",                    /* 0x02 */
277         "Compatible device ID",
278         "IRQ",                                  /* 0x04 */
279         "DMA",
280         "Dependent function start",             /* 0x06 */
281         "Dependent function end",
282         "I/O port",                             /* 0x08 */
283         "Fixed I/O port",
284         NULL,                                   /* 0x0A */
285         NULL,
286         NULL,                                   /* 0x0C */
287         NULL,
288         "Vendor tag S-0xE",                     /* 0x0E */
289         "End",
290         /* large tags +0x10 */
291         NULL,                                   /* 0x10 */
292         "Memory range",
293         "Identifier string (ANSI)",             /* 0x12 */
294         "Identifier string (Unicode)",
295         "Vendor tag L-0x4",                     /* 0x14 */
296         "32-bit memory range",
297         "32-bit fixed memory range"             /* 0x16 */
298 };
299
300 uint16_t                        isa_pnp_bios_offset = 0;
301 struct isa_pnp_bios_struct      isa_pnp_info;
302 unsigned int                    (__cdecl far *isa_pnp_rm_call)() = NULL;
303
304 #if TARGET_MSDOS == 32
305 uint16_t                        isa_pnp_pm_code_seg = 0;        /* code segment to call protected mode BIOS if */
306 uint16_t                        isa_pnp_pm_data_seg = 0;        /* data segment to call protected mode BIOS if */
307 uint8_t                         isa_pnp_pm_use = 0;             /* 1=call protected mode interface  0=call real mode interface */
308 uint8_t                         isa_pnp_pm_win95_mode = 0;      /* 1=call protected mode interface with 32-bit stack (Windows 95 style) */
309 uint16_t                        isa_pnp_rm_call_area_code_sel = 0;
310 uint16_t                        isa_pnp_rm_call_area_sel = 0;
311 void*                           isa_pnp_rm_call_area = NULL;
312 #endif
313
314 int init_isa_pnp_bios() {
315 #if TARGET_MSDOS == 32
316         isa_pnp_rm_call_area_code_sel = 0;
317         isa_pnp_rm_call_area_sel = 0;
318         isa_pnp_pm_code_seg = 0;
319         isa_pnp_pm_data_seg = 0;
320         isa_pnp_pm_use = 0;
321 #endif
322         return 1;
323 }
324
325 void free_isa_pnp_bios() {
326 #if TARGET_MSDOS == 32
327         if (isa_pnp_pm_code_seg != 0)
328                 dpmi_free_descriptor(isa_pnp_pm_code_seg);
329         if (isa_pnp_pm_data_seg != 0)
330                 dpmi_free_descriptor(isa_pnp_pm_data_seg);
331         if (isa_pnp_rm_call_area_code_sel != 0)
332                 dpmi_free_descriptor(isa_pnp_rm_call_area_code_sel);
333         if (isa_pnp_rm_call_area_sel != 0)
334                 dpmi_free_dos(isa_pnp_rm_call_area_sel);
335         isa_pnp_pm_code_seg = 0;
336         isa_pnp_pm_data_seg = 0;
337         isa_pnp_rm_call_area_sel = 0;
338         isa_pnp_rm_call_area_code_sel = 0;
339         isa_pnp_rm_call_area = NULL;
340         isa_pnp_pm_use = 0;
341 #endif
342 }
343
344 int find_isa_pnp_bios() {
345 #if TARGET_MSDOS == 32
346         uint8_t *scan = (uint8_t*)0xF0000;
347 #else
348         uint8_t far *scan = (uint8_t far*)MK_FP(0xF000U,0x0000U);
349 #endif
350         unsigned int offset,i;
351
352         free_isa_pnp_bios();
353         isa_pnp_bios_offset = (uint16_t)(~0U);
354         memset(&isa_pnp_info,0,sizeof(isa_pnp_info));
355
356         /* NTS: Stop at 0xFFE0 because 0xFFE0+0x21 >= end of 64K segment */
357         for (offset=0U;offset != 0xFFE0U;offset += 0x10U,scan += 0x10) {
358                 if (scan[0] == '$' && scan[1] == 'P' && scan[2] == 'n' &&
359                         scan[3] == 'P' && scan[4] >= 0x10 && scan[5] >= 0x21) {
360                         uint8_t chk=0;
361                         for (i=0;i < scan[5];i++)
362                                 chk += scan[i];
363
364                         if (chk == 0) {
365                                 isa_pnp_bios_offset = (uint16_t)offset;
366                                 _fmemcpy(&isa_pnp_info,scan,sizeof(isa_pnp_info));
367                                 isa_pnp_rm_call = (void far*)MK_FP(isa_pnp_info.rm_ent_segment,
368                                         isa_pnp_info.rm_ent_offset);
369
370 #if TARGET_MSDOS == 32
371                                 isa_pnp_rm_call_area = dpmi_alloc_dos(ISA_PNP_RM_DOS_AREA_SIZE,&isa_pnp_rm_call_area_sel);
372                                 if (isa_pnp_rm_call_area == NULL) {
373                                         fprintf(stderr,"WARNING: Failed to allocate common area for DOS realmode calling\n");
374                                         goto fail;
375                                 }
376
377                                 /* allocate descriptors to make calling the BIOS from pm mode */
378                                 if ((isa_pnp_pm_code_seg = dpmi_alloc_descriptor()) != 0) {
379                                         if (!dpmi_set_segment_base(isa_pnp_pm_code_seg,isa_pnp_info.pm_ent_segment_base)) {
380                                                 fprintf(stderr,"WARNING: Failed to set segment base\n"); goto fail; }
381                                         if (!dpmi_set_segment_limit(isa_pnp_pm_code_seg,0xFFFFUL)) {
382                                                 fprintf(stderr,"WARNING: Failed to set segment limit\n"); goto fail; }
383                                         if (!dpmi_set_segment_access(isa_pnp_pm_code_seg,0x9A)) {
384                                                 fprintf(stderr,"WARNING: Failed to set segment access rights\n"); goto fail; }
385                                 }
386
387                                 if ((isa_pnp_pm_data_seg = dpmi_alloc_descriptor()) != 0) {
388                                         if (!dpmi_set_segment_base(isa_pnp_pm_data_seg,isa_pnp_info.pm_ent_data_segment_base)) {
389                                                 fprintf(stderr,"WARNING: Failed to set segment base\n"); goto fail; }
390                                         if (!dpmi_set_segment_limit(isa_pnp_pm_data_seg,0xFFFFUL)) {
391                                                 fprintf(stderr,"WARNING: Failed to set segment limit\n"); goto fail; }
392                                         if (!dpmi_set_segment_access(isa_pnp_pm_data_seg,0x92)) {
393                                                 fprintf(stderr,"WARNING: Failed to set segment access rights\n"); goto fail; }
394                                 }
395
396                                 /* allocate code selector for realmode area */
397                                 if ((isa_pnp_rm_call_area_code_sel = dpmi_alloc_descriptor()) != 0) {
398                                         if (!dpmi_set_segment_base(isa_pnp_rm_call_area_code_sel,(uint32_t)isa_pnp_rm_call_area)) {
399                                                 fprintf(stderr,"WARNING: Failed to set segment base\n"); goto fail; }
400                                         if (!dpmi_set_segment_limit(isa_pnp_rm_call_area_code_sel,0xFFFFUL)) {
401                                                 fprintf(stderr,"WARNING: Failed to set segment limit\n"); goto fail; }
402                                         if (!dpmi_set_segment_access(isa_pnp_rm_call_area_code_sel,0x9A)) {
403                                                 fprintf(stderr,"WARNING: Failed to set segment access rights\n"); goto fail; }
404                                 }
405
406                                 isa_pnp_pm_use = 1;
407 #endif
408
409                                 return 1;
410 #if TARGET_MSDOS == 32
411 fail:                           free_isa_pnp_bios();
412                                 return 0;
413 #endif
414                         }
415                 }
416         }
417
418         return 0;
419 }
420
421 #if TARGET_MSDOS == 32
422
423 static unsigned int isa_pnp_thunk_and_call() { /* private, don't export */
424         unsigned short sgm = isa_pnp_pm_use ? (unsigned short)isa_pnp_rm_call_area_sel : (unsigned short)((size_t)isa_pnp_rm_call_area >> 4);
425         if (isa_pnp_pm_use) {
426                 unsigned char *callfunc = (unsigned char*)isa_pnp_rm_call_area + 0x8;
427                 unsigned short ret_ax = ~0U,my_cs = 0;
428
429                 __asm {
430                         mov             ax,cs
431                         mov             my_cs,ax
432                 }
433
434                 /* 32-bit far pointer used in call below */
435                 *((uint32_t*)(callfunc+0+0)) = 0x08+0x8;
436                 *((uint32_t*)(callfunc+0+4)) = isa_pnp_rm_call_area_code_sel;
437                 /* 386 assembly language: CALL <proc> */
438                 *((uint8_t *)(callfunc+8+0)) = 0x9A;
439                 *((uint16_t*)(callfunc+8+1)) = (uint16_t)isa_pnp_info.pm_ent_offset;
440                 *((uint16_t*)(callfunc+8+3)) = (uint16_t)isa_pnp_pm_code_seg;
441                 /* 386 assembly language: from 16-bit segment: [32-bit] call far <32-bit code segment:offset of label "asshole"> */
442                 *((uint8_t *)(callfunc+8+5)) = 0x66;
443                 *((uint8_t *)(callfunc+8+6)) = 0x67;
444                 *((uint8_t *)(callfunc+8+7)) = 0xEA;
445                 *((uint32_t*)(callfunc+8+8)) = 0;
446                 *((uint32_t*)(callfunc+8+12)) = my_cs;
447
448                 /* protected mode call */
449                 if (isa_pnp_pm_win95_mode) {
450                         /* Windows 95 mode: call just like below, but DON'T switch stacks (leave SS:ESP => 32-bit stack).
451                          * This is what Windows 95 does and it's probably why the Bochs implementation of Plug & Play BIOS
452                          * doesn't work, since everything in the PnP spec implies 16-bit FAR pointers */
453                         __asm {
454                                 pushad
455                                 push            fs
456                                 cli
457
458                                 ; These stupid acrobatics are necessary because Watcom has a hang-up
459                                 ; about inline assembly referring to stack variables and doesn't like
460                                 ; inline assembly referring to label addresses
461                                 call            acrobat1                        ; near call to put label address on stack
462                                 jmp             asshole                         ; relative JMP address to the 'asshole' label below
463 acrobat1:                       pop             eax                             ; retrieve the address of the 'acrobat1' label
464                                 add             eax,dword ptr [eax+1]           ; sum it against the relative 32-bit address portion of the JMP instruction (NTS: JMP = 0xEA <32-bit rel addr>)
465                                 mov             esi,callfunc
466                                 mov             dword ptr [esi+8+8],eax
467
468 ;                               mov             cx,ss
469                                 mov             dx,ds
470                                 mov             ebx,esp
471 ;                               mov             esi,ebp
472 ;                               xor             ebp,ebp
473                                 mov             esp,isa_pnp_rm_call_area
474                                 add             esp,0x1F0
475                                 mov             ax,isa_pnp_rm_call_area_sel
476                                 mov             fs,ax
477                                 mov             ax,isa_pnp_pm_data_seg
478                                 mov             ds,ax
479                                 mov             es,ax
480
481                                 ; call
482                                 jmpf            fword ptr fs:0x8
483
484                                 ; WARNING: do NOT remove these NOPs
485                                 nop
486                                 nop
487                                 nop
488                                 nop
489                                 nop
490                                 nop
491 asshole:
492                                 nop
493                                 nop
494                                 nop
495                                 nop
496                                 nop
497                                 nop
498                                 nop
499
500                                 ; restore stack (NTS: PnP BIOSes are supposed to restore ALL regs except AX)
501                                 cli
502 ;                               mov             ebp,esi
503                                 mov             esp,ebx
504 ;                               mov             ss,cx
505                                 mov             ds,dx
506                                 mov             es,dx
507
508                                 mov             ret_ax,ax
509
510                                 sti
511                                 pop             fs
512                                 popad
513                         }
514                 }
515                 else {
516                         __asm {
517                                 pushad
518                                 cli
519
520                                 ; These stupid acrobatics are necessary because Watcom has a hang-up
521                                 ; about inline assembly referring to stack variables and doesn't like
522                                 ; inline assembly referring to label addresses
523                                 call            acrobat1                        ; near call to put label address on stack
524                                 jmp             asshole                         ; relative JMP address to the 'asshole' label below
525 acrobat1:                       pop             eax                             ; retrieve the address of the 'acrobat1' label
526                                 add             eax,dword ptr [eax+1]           ; sum it against the relative 32-bit address portion of the JMP instruction (NTS: JMP = 0xEA <32-bit rel addr>)
527                                 mov             esi,callfunc
528                                 mov             dword ptr [esi+8+8],eax
529
530                                 mov             cx,ss
531                                 mov             dx,ds
532                                 mov             ebx,esp
533                                 mov             esi,ebp
534                                 xor             ebp,ebp
535                                 mov             esp,0x1F0
536                                 mov             ax,isa_pnp_rm_call_area_sel
537                                 mov             ss,ax
538                                 mov             ax,isa_pnp_pm_data_seg
539                                 mov             ds,ax
540                                 mov             es,ax
541
542                                 ; call
543                                 jmpf            fword ptr ss:0x8
544
545                                 ; WARNING: do NOT remove these NOPs
546                                 nop
547                                 nop
548                                 nop
549                                 nop
550                                 nop
551                                 nop
552 asshole:
553                                 nop
554                                 nop
555                                 nop
556                                 nop
557                                 nop
558                                 nop
559                                 nop
560
561                                 ; restore stack (NTS: PnP BIOSes are supposed to restore ALL regs except AX)
562                                 cli
563                                 mov             ebp,esi
564                                 mov             esp,ebx
565                                 mov             ss,cx
566                                 mov             ds,dx
567                                 mov             es,dx
568
569                                 mov             ret_ax,ax
570
571                                 sti
572                                 popad
573                         }
574                 }
575
576                 return ret_ax;
577         }
578         else {
579                 /* real-mode call via DPMI */
580                 /* ..but wait---we might be running under DOS4/G which doesn't
581                    provide the "real mode call" function! */
582                 struct dpmi_realmode_call d={0};
583                 unsigned int x = (unsigned int)(&d); /* NTS: Work around Watcom's "cannot take address of stack symbol" error */
584                 d.cs = isa_pnp_info.rm_ent_segment;
585                 d.ip = isa_pnp_info.rm_ent_offset;
586                 d.ss = sgm;                             /* our real-mode segment is also the stack during the call */
587                 d.sp = 0x1F0;
588
589                 if (dpmi_no_0301h < 0) probe_dpmi();
590
591                 if (dpmi_no_0301h > 0) {
592                         /* Fuck you DOS4/GW! */
593                         dpmi_alternate_rm_call_stacko(&d);
594                 }
595                 else {
596                         __asm {
597                                 pushad
598
599                                 push            ds
600                                 pop             es
601
602                                 mov             eax,0x0301              ; DPMI call real-mode far routine
603                                 xor             ebx,ebx
604                                 xor             ecx,ecx
605                                 mov             edi,x
606                                 int             0x31
607
608                                 popad
609                         }
610                 }
611
612                 if (!(d.flags & 1))
613                         return d.eax&0xFF;
614         }
615
616         return ~0U;
617 }
618
619 unsigned int isa_pnp_bios_number_of_sysdev_nodes(unsigned char far *a,unsigned int far *b) {
620         /* use the DOS segment we allocated as a "trampoline" for the 16-bit code to write the return values to */
621         /* SEG:0x0000 = a
622          * SEG:0x0004 = b
623          * SEG:0x00F0 = stack for passing params */
624         unsigned int *rb_a = (unsigned int*)((unsigned char*)isa_pnp_rm_call_area);
625         unsigned int *rb_b = (unsigned int*)((unsigned char*)isa_pnp_rm_call_area + 4);
626         unsigned short *stk = (unsigned short*)((unsigned char*)isa_pnp_rm_call_area + 0x1F0);
627         unsigned short sgm = isa_pnp_pm_use ? (unsigned short)isa_pnp_rm_call_area_sel : (unsigned short)((size_t)isa_pnp_rm_call_area >> 4);
628         unsigned int ret_ax = ~0;
629
630         *rb_a = ~0UL; *rb_b = ~0UL;
631
632         /* build the stack */
633         stk[5] = isa_pnp_pm_use ? (unsigned short)isa_pnp_pm_data_seg : (unsigned short)isa_pnp_info.rm_ent_data_segment; /* BIOS data segment */
634         stk[4] = sgm;
635         stk[3] = 4;     /* offset of "b" (segment will be filled in separately) */
636         stk[2] = sgm;
637         stk[1] = 0;     /* offset of "a" (ditto) */
638         stk[0] = 0;     /* function */
639
640         ret_ax = isa_pnp_thunk_and_call();
641         if (ret_ax == ~0U) {
642                 fprintf(stderr,"WARNING: Thunk and call failed\n");
643         }
644         else {
645                 *a = (unsigned char)(*rb_a);
646                 *b = *rb_b & 0xFFFF;
647                 return ret_ax&0xFF;
648         }
649
650         return ~0;
651 }
652
653 unsigned int isa_pnp_bios_get_sysdev_node(unsigned char far *a,unsigned char far *b,unsigned int c) {
654         /* use the DOS segment we allocated as a "trampoline" for the 16-bit code to write the return values to */
655         /* SEG:0x0000 = a (node)
656          * SEG:0x00F0 = stack for passing params
657          * SEG:0x0100 = b (buffer for xfer)
658          * SEF:0x0FFF = end */
659         unsigned char *rb_a = (unsigned char*)((unsigned char*)isa_pnp_rm_call_area);
660         unsigned char *rb_b = (unsigned char*)((unsigned char*)isa_pnp_rm_call_area + 0x200);
661         unsigned short *stk = (unsigned short*)((unsigned char*)isa_pnp_rm_call_area + 0x1F0);
662         unsigned short sgm = isa_pnp_pm_use ? (unsigned short)isa_pnp_rm_call_area_sel : (unsigned short)((size_t)isa_pnp_rm_call_area >> 4);
663         unsigned int ret_ax = ~0;
664
665         *rb_a = *a;
666         rb_b[0] = rb_b[1] = 0;
667
668         stk[6] = isa_pnp_pm_use ? (unsigned short)isa_pnp_pm_data_seg : (unsigned short)isa_pnp_info.rm_ent_data_segment; /* BIOS data segment */
669         stk[5] = c;
670         stk[4] = sgm;   /* offset of "b" */
671         stk[3] = 0x200;
672         stk[2] = sgm;   /* offset of "a" */
673         stk[1] = 0;
674         stk[0] = 1;     /* function */
675
676         ret_ax = isa_pnp_thunk_and_call();
677         if (ret_ax == ~0U) {
678                 fprintf(stderr,"WARNING: Thunk and call failed\n");
679         }
680         else {
681                 unsigned int len = *((uint16_t*)rb_b);  /* read back the size of the device node */
682                 *a = (unsigned char)(*rb_a);
683                 if ((0x100+len) > ISA_PNP_RM_DOS_AREA_SIZE) {
684                         fprintf(stderr,"Whoahhhh..... the returned device struct is too large! len=%u\n",len);
685                         return 0xFF;
686                 }
687                 else {
688                         if ((ret_ax&0xFF) == 0) _fmemcpy(b,rb_b,len);
689                 }
690                 return ret_ax&0xFF;
691         }
692
693         return ~0;
694 }
695
696 unsigned int isa_pnp_bios_get_static_alloc_resinfo(unsigned char far *a) {
697         return ~0;
698 }
699
700 unsigned int isa_pnp_bios_get_pnp_isa_cfg(unsigned char far *a) {
701         /* use the DOS segment we allocated as a "trampoline" for the 16-bit code to write the return values to */
702         /* SEG:0x0000 = a (node)
703          * SEG:0x00F0 = stack for passing params
704          * SEG:0x0100 = b (buffer for xfer)
705          * SEF:0x0FFF = end */
706         unsigned char *rb_a = (unsigned char*)((unsigned char*)isa_pnp_rm_call_area + 0x200);
707         unsigned short *stk = (unsigned short*)((unsigned char*)isa_pnp_rm_call_area + 0x1F0);
708         unsigned short sgm = isa_pnp_pm_use ? (unsigned short)isa_pnp_rm_call_area_sel : (unsigned short)((size_t)isa_pnp_rm_call_area >> 4);
709         unsigned int ret_ax = ~0;
710
711         stk[3] = isa_pnp_pm_use ? (unsigned short)isa_pnp_pm_data_seg : (unsigned short)isa_pnp_info.rm_ent_data_segment; /* BIOS data segment */
712         stk[2] = sgm;   /* offset of "a" */
713         stk[1] = 0x200;
714         stk[0] = 0x40;  /* function */
715
716         ret_ax = isa_pnp_thunk_and_call();
717         if (ret_ax == ~0U) {
718                 fprintf(stderr,"WARNING: Thunk and call failed\n");
719         }
720         else {
721                 if ((ret_ax&0xFF) == 0) _fmemcpy(a,rb_a,sizeof(struct isapnp_pnp_isa_cfg));
722                 return ret_ax&0xFF;
723         }
724
725         return ~0;
726 }
727
728 /* UPDATE 2011/05/27: Holy shit, found an ancient Pentium Pro system who's BIOS implements the ESCD functions! */
729 unsigned int isa_pnp_bios_get_escd_info(unsigned int far *min_escd_write_size,unsigned int far *escd_size,unsigned long far *nv_storage_base) {
730         /* use the DOS segment we allocated as a "trampoline" for the 16-bit code to write the return values to */
731         /* SEG:0x0000 = a (node)
732          * SEG:0x00F0 = stack for passing params
733          * SEG:0x0100 = b (buffer for xfer)
734          * SEF:0x0FFF = end */
735         unsigned short *rb_a = (unsigned short*)((unsigned char*)isa_pnp_rm_call_area + 0x200);
736         unsigned short *rb_b = (unsigned short*)((unsigned char*)isa_pnp_rm_call_area + 0x204);
737         unsigned long  *rb_c = (unsigned long*)((unsigned char*)isa_pnp_rm_call_area + 0x208);
738         unsigned short *stk = (unsigned short*)((unsigned char*)isa_pnp_rm_call_area + 0x1F0);
739         unsigned short sgm = isa_pnp_pm_use ? (unsigned short)isa_pnp_rm_call_area_sel : (unsigned short)((size_t)isa_pnp_rm_call_area >> 4);
740         unsigned int ret_ax = ~0;
741
742         *rb_a = ~0UL;
743         *rb_b = ~0UL;
744         *rb_c = ~0UL;
745
746         stk[7] = isa_pnp_pm_use ? (unsigned short)isa_pnp_pm_data_seg : (unsigned short)isa_pnp_info.rm_ent_data_segment; /* BIOS data segment */
747
748         stk[6] = sgm;   /* offset of "c" */
749         stk[5] = 0x208;
750
751         stk[4] = sgm;   /* offset of "b" */
752         stk[3] = 0x204;
753
754         stk[2] = sgm;   /* offset of "a" */
755         stk[1] = 0x200;
756
757         stk[0] = 0x41;  /* function */
758
759         ret_ax = isa_pnp_thunk_and_call();
760         if (ret_ax == ~0U) {
761                 fprintf(stderr,"WARNING: Thunk and call failed\n");
762         }
763         else {
764                 if ((ret_ax&0xFF) == 0) {
765                         *min_escd_write_size = *rb_a;
766                         *escd_size = *rb_b;
767                         *nv_storage_base = *rb_c;
768                 }
769                 return ret_ax&0xFF;
770         }
771
772         return ~0;
773 }
774
775 unsigned int isa_pnp_bios_send_message(unsigned int msg) {
776         /* use the DOS segment we allocated as a "trampoline" for the 16-bit code to write the return values to */
777         /* SEG:0x0000 = a (node)
778          * SEG:0x00F0 = stack for passing params
779          * SEG:0x0100 = b (buffer for xfer)
780          * SEF:0x0FFF = end */
781         unsigned short *stk = (unsigned short*)((unsigned char*)isa_pnp_rm_call_area + 0x1F0);
782         unsigned int ret_ax = ~0;
783
784         stk[2] = isa_pnp_pm_use ? (unsigned short)isa_pnp_pm_data_seg : (unsigned short)isa_pnp_info.rm_ent_data_segment; /* BIOS data segment */
785         stk[1] = msg;
786
787         stk[0] = 0x04;  /* function */
788
789         ret_ax = isa_pnp_thunk_and_call();
790         if (ret_ax == ~0U) {
791                 fprintf(stderr,"WARNING: Thunk and call failed\n");
792         }
793         else {
794                 return ret_ax&0xFF;
795         }
796
797         return ~0U;
798 }
799
800 #endif
801
802 void isa_pnp_product_id_to_str(char *str,unsigned long id) {
803         sprintf(str,"%c%c%c%X%X%X%X",
804                 (unsigned char)(0x40+((id>>2)&0x1F)),
805                 (unsigned char)(0x40+((id&3)<<3)+((id>>13)&7)),
806                 (unsigned char)(0x40+((id>>8)&0x1F)),
807                 (unsigned char)((id>>20)&0xF),
808                 (unsigned char)((id>>16)&0xF),
809                 (unsigned char)((id>>28)&0xF),
810                 (unsigned char)((id>>24)&0xF));
811 }
812
813 const char *isapnp_tag_str(uint8_t tag) { /* tag from struct isapnp_tag NOT the raw byte */
814         if (tag >= (sizeof(isapnp_tag_strs)/sizeof(isapnp_tag_strs[0])))
815                 return NULL;
816
817         return isapnp_tag_strs[tag];
818 }
819
820 const char *isapnp_sdf_priority_str(uint8_t x) {
821         if (x >= 3) return NULL;
822         return isapnp_sdf_priority_strs[x];
823 }
824
825 int isapnp_read_tag(uint8_t far **pptr,uint8_t far *fence,struct isapnp_tag *tag) {
826         uint8_t far *ptr = *pptr;
827         unsigned char c;
828
829         if (ptr >= fence)
830                 return 0;
831
832         c = *ptr++;
833         if (c & 0x80) { /* large tag */
834                 tag->tag = 0x10 + (c & 0x7F);
835                 tag->len = *((uint16_t far*)(ptr));
836                 ptr += 2;
837                 tag->data = ptr;
838                 ptr += tag->len;
839                 if (ptr > fence)
840                         return 0;
841         }
842         else { /* small tag */
843                 tag->tag = (c >> 3) & 0xF;
844                 tag->len = c & 7;
845                 tag->data = ptr;
846                 ptr += tag->len;
847                 if (ptr > fence)
848                         return 0;
849         }
850
851         *pptr = ptr;
852         return 1;
853 }
854
855 const char *isa_pnp_device_category(uint32_t productid) {
856         char tmp[8]; isa_pnp_product_id_to_str(tmp,productid);
857         if (!memcmp(tmp,"PNP",3)) {
858                 switch (tmp[3]) {
859                         case '0': switch(tmp[4]) {
860                                 case '0': return "System device, interrupt controller";
861                                 case '1': return "System device, timer";
862                                 case '2': return "System device, DMA controller";
863                                 case '3': return "System device, keyboard";
864                                 case '4': return "System device, parallel port";
865                                 case '5': return "System device, serial port";
866                                 case '6': return "System device, hard disk controller";
867                                 case '7': return "System device, floppy disk controller";
868                                 case '9': return "System device, display adapter";
869                                 case 'A': return "System device, peripheral bus";
870                                 case 'C': return "System device, motherboard device";
871                                 case 'E': return "System device, PCMCIA controller";
872                                 case 'F': return "System device, mouse";
873                                 default:  return "System devices";
874                         }
875                         case '8': return "Network devices";
876                         case 'A': return "SCSI & non-standard CD-ROM devices";
877                         case 'B': return "Sound, video & multimedia";
878                         case 'C': return "Modem devices";
879                 };
880         }
881
882         return NULL;
883 }
884
885 void isa_pnp_device_description(char desc[255],uint32_t productid) {
886         char tmp[8]; isa_pnp_product_id_to_str(tmp,productid);
887         desc[0] = 0;
888
889         if (!memcmp(tmp,"PNP",3)) {
890                 unsigned int hex = (unsigned int)strtol(tmp+3,0,16),hexm = 0;
891                 const char *rets = NULL,**arr = NULL;
892                 unsigned int ars = 0;
893                 if (hex == 0x0800) {
894                         rets = "PC speaker";
895                 }
896                 else if (hex == 0x0B00) {
897                         rets = "Realtime clock";
898                 }
899                 else if (hex == 0x09FF) {
900                         rets = "Plug & Play DDC monitor";
901                 }
902                 else {
903                         switch (hex>>8) {
904 #define PNP(a) \
905         case a: ars = sizeof(isa_pnp_devd_pnp_##a##xx)/sizeof(isa_pnp_devd_pnp_##a##xx[0]); arr = isa_pnp_devd_pnp_##a##xx; hexm = 0xFF; break
906                                 PNP(0x00); /* PNP00xx */        PNP(0x01); /* PNP01xx */
907                                 PNP(0x02); /* PNP02xx */        PNP(0x03); /* PNP03xx */
908                                 PNP(0x04); /* PNP04xx */        PNP(0x05); /* PNP05xx */
909                                 PNP(0x06); /* PNP06xx */        PNP(0x07); /* PNP07xx */
910                                 PNP(0x09); /* PNP09xx */        PNP(0x0A); /* PNP0Axx */
911                                 PNP(0x0C); /* PNP0Cxx */        PNP(0x0E); /* PNP0Exx */
912                                 PNP(0x0F); /* PNP0Fxx */
913 #undef PNP
914                         }
915                 };
916
917                 if (rets == NULL && ars != (size_t)0 && arr != NULL && (hex&hexm) < ars)
918                         rets = arr[hex&hexm];
919
920                 if (rets != NULL)
921                         strcpy(desc,rets);
922         }
923 }
924
925 const char *isa_pnp_device_type(const uint8_t far *b,const char **subtype,const char **inttype) {
926         const char *st = NULL;
927         const char *it = NULL;
928         const char *t = NULL;
929
930         switch (b[0]) {
931                 case 1: t = "Mass Storage Device";
932                         switch (b[1]) {
933                                 case 0: st = "SCSI controller"; break;
934                                 case 1: st = "IDE controller (Standard ATA compatible)";
935                                         switch (b[2]) {
936                                                 case 0: it = "Generic IDE"; break;
937                                         };
938                                         break;
939                                 case 2: st = "Floppy controller (Standard 765 compatible)";
940                                         switch (b[2]) {
941                                                 case 0: it = "Generic floppy"; break;
942                                         };
943                                         break;
944                                 case 3: st = "IPI controller";
945                                         switch (b[2]) {
946                                                 case 0: it = "Generic"; break;
947                                         };
948                                         break;
949                                 case 0x80:
950                                         st = "Other";
951                                         break;
952                         };
953                         break;
954                 case 2: t = "Network Interface Controller";
955                         switch (b[1]) {
956                                 case 0: st = "Ethernet controller";
957                                         switch (b[2]) {
958                                                 case 0: it = "Generic"; break;
959                                         };
960                                         break;
961                                 case 1: st = "Token ring controller";
962                                         switch (b[2]) {
963                                                 case 0: it = "Generic"; break;
964                                         };
965                                         break;
966                                 case 2: st = "FDDI controller";
967                                         switch (b[2]) {
968                                                 case 0: it = "Generic"; break;
969                                         };
970                                         break;
971                                 case 0x80:
972                                         st = "Other";
973                                         break;
974                         };
975                         break;
976                 case 3: t = "Display Controller";
977                         switch (b[1]) {
978                                 case 0: st = "VGA controller (Standard VGA compatible)";
979                                         switch (b[2]) {
980                                                 case 0: it = "Generic VGA compatible"; break;
981                                                 case 1: it = "VESA SVGA compatible"; break;
982                                         };
983                                         break;
984                                 case 1: st = "XGA compatible controller";
985                                         switch (b[2]) {
986                                                 case 0: it = "General XGA compatible"; break;
987                                         };
988                                         break;
989                                 case 0x80:
990                                         st = "Other";
991                                         break;
992                         };
993                         break;
994                 case 4: t = "Multimedia Controller";
995                         switch (b[1]) {
996                                 case 0: st = "Video controller";
997                                         switch (b[2]) {
998                                                 case 0: it = "General video"; break;
999                                         };
1000                                         break;
1001                                 case 1: st = "Audio controller";
1002                                         switch (b[2]) {
1003                                                 case 0: it = "General audio"; break;
1004                                         };
1005                                         break;
1006                                 case 0x80:
1007                                         st = "Other";
1008                                         break;
1009                         };
1010                         break;
1011                 case 5: t = "Memory";
1012                         switch (b[1]) {
1013                                 case 0: st = "RAM";
1014                                         switch (b[2]) {
1015                                                 case 0: it = "General RAM"; break;
1016                                         };
1017                                         break;
1018                                 case 1: st = "Flash memory";
1019                                         switch (b[2]) {
1020                                                 case 0: it = "General flash memory"; break;
1021                                         };
1022                                         break;
1023                                 case 0x80:
1024                                         st = "Other";
1025                                         break;
1026                         };
1027                         break;
1028                 case 6: t = "Bridge controller";
1029                         switch (b[1]) {
1030                                 case 0: st = "Host processor bridge";
1031                                         switch (b[2]) {
1032                                                 case 0: it = "General"; break;
1033                                         };
1034                                         break;
1035                                 case 1: st = "ISA bridge";
1036                                         switch (b[2]) {
1037                                                 case 0: it = "General"; break;
1038                                         };
1039                                         break;
1040                                 case 2: st = "EISA bridge";
1041                                         switch (b[2]) {
1042                                                 case 0: it = "General"; break;
1043                                         };
1044                                         break;
1045                                 case 3: st = "MCA bridge";
1046                                         switch (b[2]) {
1047                                                 case 0: it = "General"; break;
1048                                         };
1049                                         break;
1050                                 case 4: st = "PCI bridge";
1051                                         switch (b[2]) {
1052                                                 case 0: it = "General"; break;
1053                                         };
1054                                         break;
1055                                 case 5: st = "PCMCIA bridge";
1056                                         switch (b[2]) {
1057                                                 case 0: it = "General"; break;
1058                                         };
1059                                         break;
1060                                 case 0x80:
1061                                         st = "Other";
1062                                         break;
1063                         };
1064                         break;
1065                 case 7: t = "Communications device";
1066                         switch (b[1]) {
1067                                 case 0: st = "RS-232";
1068                                         switch (b[2]) {
1069                                                 case 0: it = "Generic"; break;
1070                                                 case 1: it = "16450-compatible"; break;
1071                                                 case 2: it = "16550-compatible"; break;
1072                                         };
1073                                         break;
1074                                 case 1: st = "Parallel port (AT compatible)";
1075                                         switch (b[2]) {
1076                                                 case 0: it = "General"; break;
1077                                                 case 1: it = "Model 30 bidirectional port"; break;
1078                                                 case 2: it = "ECP 1.x port"; break;
1079                                         };
1080                                         break;
1081                                 case 0x80:
1082                                         st = "Other";
1083                                         break;
1084                         };
1085                         break;
1086                 case 8: t = "System peripheral";
1087                         switch (b[1]) {
1088                                 case 0: st = "Programmable Interrupt Controller (8259 compatible)";
1089                                         switch (b[2]) {
1090                                                 case 0: it = "Generic"; break;
1091                                                 case 1: it = "ISA"; break;
1092                                                 case 2: it = "EISA"; break;
1093                                         };
1094                                         break;
1095                                 case 1: st = "DMA controller (8237 compatible)";
1096                                         switch (b[2]) {
1097                                                 case 0: it = "General"; break;
1098                                                 case 1: it = "ISA"; break;
1099                                                 case 2: it = "EISA"; break;
1100                                         };
1101                                         break;
1102                                 case 2: st = "System Timer (8254 compatible)";
1103                                         switch (b[2]) {
1104                                                 case 0: it = "General"; break;
1105                                                 case 1: it = "ISA"; break;
1106                                                 case 2: it = "EISA"; break;
1107                                         };
1108                                         break;
1109                                 case 3: st = "Real-time clock";
1110                                         switch (b[2]) {
1111                                                 case 0: it = "Generic"; break;
1112                                                 case 1: it = "ISA"; break;
1113                                         };
1114                                         break;
1115                                 case 0x80:
1116                                         st = "Other";
1117                                         break;
1118                         };
1119                         break;
1120                 case 9: t = "Input device";
1121                         switch (b[1]) {
1122                                 case 0: st = "Keyboard controller";
1123                                         switch (b[2]) {
1124                                                 case 0: it = "N/A"; break;
1125                                         };
1126                                         break;
1127                                 case 1: st = "Digitizer (pen)";
1128                                         switch (b[2]) {
1129                                                 case 0: it = "N/A"; break;
1130                                         };
1131                                         break;
1132                                 case 2: st = "Mouse controller";
1133                                         switch (b[2]) {
1134                                                 case 0: it = "N/A"; break;
1135                                         };
1136                                         break;
1137                                 case 0x80:
1138                                         st = "Other";
1139                                         break;
1140                         };
1141                         break;
1142                 case 10: t = "Docking station";
1143                         switch (b[1]) {
1144                                 case 0: st = "Generic";
1145                                         switch (b[2]) {
1146                                                 case 0: it = "N/A"; break;
1147                                         };
1148                                         break;
1149                                 case 0x80:
1150                                         st = "Other";
1151                                         break;
1152                         };
1153                         break;
1154                 case 11: t = "CPU type";
1155                         switch (b[1]) {
1156                                 case 0: st = "386";
1157                                         switch (b[2]) {
1158                                                 case 0: it = "N/A"; break;
1159                                         };
1160                                         break;
1161                                 case 1: st = "486";
1162                                         switch (b[2]) {
1163                                                 case 0: it = "N/A"; break;
1164                                         };
1165                                         break;
1166                                 case 2: st = "586";
1167                                         switch (b[2]) {
1168                                                 case 0: it = "N/A"; break;
1169                                         };
1170                                         break;
1171                                 case 0x80:
1172                                         st = "Other";
1173                                         break;
1174                         };
1175                         break;
1176
1177         };
1178
1179         if (subtype != NULL)
1180                 *subtype = st;
1181         if (inttype != NULL)
1182                 *inttype = it;
1183
1184         return t;
1185 }
1186
1187 void isa_pnp_init_key() {
1188         unsigned int i;
1189
1190         for (i=0;i < 4;i++)
1191                 isa_pnp_write_address(0);
1192         for (i=0;i < 32;i++)
1193                 isa_pnp_write_address(isa_pnp_init_keystring[i]);
1194
1195         /* software must delay 1ms prior to starting the first pair of isolation reads and must wait
1196          * 250us between each subsequent pair of isolation reads. this delay gives the ISA card time
1197          * to access information for possibly very slow storage devices */
1198         t8254_wait(t8254_us2ticks(1200));
1199 }
1200
1201 void isa_pnp_wake_csn(unsigned char id) {
1202         isa_pnp_write_address(3); /* Wake[CSN] */
1203         isa_pnp_write_data(id); /* isolation state */
1204 }
1205
1206 /* the caller is expected to specify which card by calling isa_pnp_wake_csn() */
1207 int isa_pnp_init_key_readback(unsigned char *data/*9 bytes*/) {
1208         unsigned char checksum = 0x6a;
1209         unsigned char b,c1,c2,bit;
1210         unsigned int i,j;
1211
1212         isa_pnp_write_address(1); /* serial isolation reg */
1213         for (i=0;i < 9;i++) {
1214                 b = 0;
1215                 for (j=0;j < 8;j++) {
1216                         c1 = isa_pnp_read_data();
1217                         c2 = isa_pnp_read_data();
1218                         if (c1 == 0x55 && c2 == 0xAA) {
1219                                 b |= 1 << j;
1220                                 bit = 1;
1221                         }
1222                         else {
1223                                 bit = 0;
1224                         }
1225
1226                         if (i < 8)
1227                                 checksum = ((((checksum ^ (checksum >> 1)) & 1) ^ bit) << 7) | (checksum >> 1);
1228
1229                         t8254_wait(t8254_us2ticks(275));
1230                 }
1231                 data[i] = b;
1232         }
1233
1234         return (checksum == data[8]);
1235 }
1236
1237 void isa_pnp_set_read_data_port(uint16_t port) {
1238         isa_pnp_write_address(0x00);    /* RD_DATA port */
1239         isa_pnp_write_data(port >> 2);
1240 }
1241
1242 unsigned char isa_pnp_read_config() {
1243         unsigned int patience = 20;
1244         unsigned char ret = 0xFF;
1245
1246         do {
1247                 isa_pnp_write_address(0x05);
1248                 if (isa_pnp_read_data() & 1) {
1249                         isa_pnp_write_address(0x04);
1250                         ret = isa_pnp_read_data();
1251                         break;
1252                 }
1253                 else {
1254                         if (--patience == 0) break;
1255                         t8254_wait(t8254_us2ticks(100));
1256                 }
1257         } while (1);
1258         return ret;
1259 }
1260
1261 uint8_t isa_pnp_read_data_register(uint8_t x) {
1262         isa_pnp_write_address(x);
1263         return isa_pnp_read_data();
1264 }
1265
1266 void isa_pnp_write_data_register(uint8_t x,uint8_t data) {
1267         isa_pnp_write_address(0x00);
1268         isa_pnp_write_address(x);
1269         isa_pnp_write_data(data);
1270 }
1271
1272 int isa_pnp_read_io_resource(unsigned int i) {
1273         uint16_t ret;
1274
1275         if (i >= 8) return -1;
1276         ret  = (uint16_t)isa_pnp_read_data_register(0x60 + (i*2)) << 8;
1277         ret |= (uint16_t)isa_pnp_read_data_register(0x60 + (i*2) + 1);
1278         return ret;
1279 }
1280
1281 void isa_pnp_write_io_resource(unsigned int i,uint16_t port) {
1282         if (i >= 8) return;
1283         isa_pnp_write_data_register(0x60 + (i*2),port >> 8);
1284         isa_pnp_write_data_register(0x60 + (i*2) + 1,port);
1285         t8254_wait(t8254_us2ticks(250));
1286 }
1287
1288 int isa_pnp_read_irq(unsigned int i) {
1289         uint8_t c;
1290
1291         if (i >= 2) return -1;
1292         c = isa_pnp_read_data_register(0x70 + (i*2));
1293         if (c == 0xFF) return -1;
1294         if ((c & 15) == 0) return -1;   /* not assigned */
1295         return (c & 15);
1296 }
1297
1298 void isa_pnp_write_irq(unsigned int i,int irq) {
1299         if (i >= 2) return;
1300         if (irq < 0) irq = 0;
1301         isa_pnp_write_data_register(0x70 + (i*2),irq);
1302         t8254_wait(t8254_us2ticks(250));
1303 }
1304
1305 void isa_pnp_write_irq_mode(unsigned int i,unsigned int im) {
1306         if (i >= 2) return;
1307         isa_pnp_write_data_register(0x70 + (i*2) + 1,im);
1308         t8254_wait(t8254_us2ticks(250));
1309 }
1310
1311 int isa_pnp_read_dma(unsigned int i) {
1312         uint8_t c;
1313
1314         if (i >= 2) return -1;
1315         c = isa_pnp_read_data_register(0x74 + i);
1316         if (c == 0xFF) return -1;
1317         if ((c & 7) == 4) return -1;    /* not assigned */
1318         return (c & 7);
1319 }
1320
1321 void isa_pnp_write_dma(unsigned int i,int dma) {
1322         if (i >= 2) return;
1323         if (dma < 0) dma = 4;
1324         isa_pnp_write_data_register(0x74 + i,dma);
1325         t8254_wait(t8254_us2ticks(250));
1326 }