1 /*
2  * hs-win32-pci.c - HardSID PCI support for WIN32.
3  *
4  * Written by
5  *  Marco van den Heuvel <blackystardust68@yahoo.com>
6  *
7  * This file is part of VICE, the Versatile Commodore Emulator.
8  * See README for copyright notice.
9  *
10  *  This program is free software; you can redistribute it and/or modify
11  *  it under the terms of the GNU General Public License as published by
12  *  the Free Software Foundation; either version 2 of the License, or
13  *  (at your option) any later version.
14  *
15  *  This program is distributed in the hope that it will be useful,
16  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
17  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  *  GNU General Public License for more details.
19  *
20  *  You should have received a copy of the GNU General Public License
21  *  along with this program; if not, write to the Free Software
22  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
23  *  02111-1307  USA.
24  *
25  */
26 
27 /* Tested and confirmed working on:
28 
29  - Windows 95C (PCI HardSID, Direct PCI I/O)
30  - Windows 95C (PCI HardSID Quattro, Direct PCI I/O)
31  - Windows 98SE (PCI HardSID, Direct PCI I/O)
32  - Windows 98SE (PCI HardSID Quattro, Direct PCI I/O)
33  - Windows ME (PCI HardSID, Direct PCI I/O)
34  - Windows ME (PCI HardSID Quattro, Direct PCI I/O)
35  - Windows NT 4 (PCI HardSID, winio32.dll PCI I/O)
36  - Windows NT 4 (PCI HardSID Quattro, winio32.dll PCI I/O)
37  - Windows 2000 (PCI HardSID, winio32.dll PCI I/O)
38  - Windows 2000 (PCI HardSID Quattro, winio32.dll PCI I/O)
39  - Windows XP (PCI HardSID, winio32.dll PCI I/O)
40  - Windows XP (PCI HardSID Quattro, winio32.dll PCI I/O)
41  - Windows 2003 Server (PCI HardSID, winio32.dll PCI I/O)
42  - Windows 2003 Server (PCI HardSID Quattro, winio32.dll PCI I/O)
43  */
44 
45 #include "vice.h"
46 
47 #ifdef WIN32_COMPILE
48 
49 #ifdef HAVE_HARDSID
50 #include <windows.h>
51 #include <fcntl.h>
52 #include <stdio.h>
53 #include <assert.h>
54 
55 #ifdef HAVE_UNISTD_H
56 #include <unistd.h>
57 #endif
58 
59 #include "alarm.h"
60 #include "archdep.h"
61 #include "hardsid.h"
62 #include "hs-win32.h"
63 #include "log.h"
64 #include "sid-resources.h"
65 #include "types.h"
66 #include "wininpoutp.h"
67 
68 #define MAXSID 4
69 
70 static int sids_found = -1;
71 static int hssids[MAXSID] = {-1, -1, -1, -1};
72 
73 static int io1 = 0;
74 static int io2 = 0;
75 
76 static int hardsid_use_lib = 0;
77 
78 #ifndef MSVC_RC
79 typedef int _stdcall (*initfuncPtr)(void);
80 typedef void _stdcall (*shutdownfuncPtr)(void);
81 typedef int _stdcall (*inpfuncPtr)(WORD port, PDWORD value, BYTE size);
82 typedef int _stdcall (*oupfuncPtr)(WORD port, DWORD value, BYTE size);
83 #else
84 typedef int (CALLBACK* initfuncPtr)(void);
85 typedef void (CALLBACK* shutdownfuncPtr)(void);
86 typedef int (CALLBACK* inpfuncPtr)(WORD, PDWORD, BYTE);
87 typedef int (CALLBACK* oupfuncPtr)(WORD, DWORD, BYTE);
88 #endif
89 
90 static initfuncPtr init32fp;
91 static shutdownfuncPtr shutdown32fp;
92 static inpfuncPtr inp32fp;
93 static oupfuncPtr oup32fp;
94 
95 /* input/output functions */
hardsid_outb(unsigned int addrint,DWORD value)96 static void hardsid_outb(unsigned int addrint, DWORD value)
97 {
98     WORD addr = (WORD)addrint;
99 
100     /* make sure the above conversion did not loose any details */
101     assert(addr == addrint);
102 
103 #ifdef  _M_IX86
104 #ifdef WATCOM_COMPILE
105     outp(addr, (BYTE)value);
106 #else
107     _outp(addr, (BYTE)value);
108 #endif
109 #endif
110 }
111 
hardsid_outl(unsigned int addrint,DWORD value)112 static void hardsid_outl(unsigned int addrint, DWORD value)
113 {
114     WORD addr = (WORD)addrint;
115 
116     /* make sure the above conversion did not loose any details */
117     assert(addr == addrint);
118 
119 #ifdef  _M_IX86
120 #ifdef WATCOM_COMPILE
121     outpd(addr, value);
122 #else
123     _outpd(addr, value);
124 #endif
125 #endif
126 }
127 
hardsid_inb(unsigned int addrint)128 static BYTE hardsid_inb(unsigned int addrint)
129 {
130     WORD addr = (WORD)addrint;
131 
132     /* make sure the above conversion did not loose any details */
133     assert(addr == addrint);
134 
135 #ifdef  _M_IX86
136 #ifdef WATCOM_COMPILE
137     return inp(addr);
138 #else
139     return _inp(addr);
140 #endif
141 #endif
142     return 0;
143 }
144 
hardsid_inl(unsigned int addrint)145 static DWORD hardsid_inl(unsigned int addrint)
146 {
147     WORD addr = (WORD)addrint;
148 
149     /* make sure the above conversion did not loose any details */
150     assert(addr == addrint);
151 
152 #ifdef  _M_IX86
153 #ifdef WATCOM_COMPILE
154     return inpd(addr);
155 #else
156     return _inpd(addr);
157 #endif
158 #endif
159     return 0;
160 }
161 
hs_pci_read(uint16_t addr,int chipno)162 int hs_pci_read(uint16_t addr, int chipno)
163 {
164     uint8_t ret = 0;
165 
166     if (chipno < MAXSID && hssids[chipno] != -1 && addr < 0x20) {
167         hardsid_outb(io1 + 4, (BYTE)((chipno << 6) | (addr & 0x1f) | 0x20));
168         vice_usleep(2);
169         hardsid_outb(io2 + 2, 0x20);
170         ret = hardsid_inb(io1);
171         hardsid_outb(io2 + 2, 0x80);
172     }
173     return ret;
174 }
175 
hs_pci_store(uint16_t addr,uint8_t outval,int chipno)176 void hs_pci_store(uint16_t addr, uint8_t outval, int chipno)
177 {
178     if (chipno < MAXSID && hssids[chipno] != -1 && addr < 0x20) {
179         hardsid_outb(io1 + 3, outval);
180         hardsid_outb(io1 + 4, (BYTE)((chipno << 6) | (addr & 0x1f)));
181         vice_usleep(2);
182     }
183 }
184 
185 /*----------------------------------------------------------------------*/
186 
187 static HINSTANCE hLib = NULL;
188 
189 #ifdef _MSC_VER
190 #  ifdef _WIN64
191 #    define INPOUTDLLNAME "winio64.dll"
192 #  else
193 #    define INPOUTDLLNAME "winio32.dll"
194 #    define INPOUTDLLOLDNAME "winio.dll"
195 #  endif
196 #else
197 #  if defined(__amd64__) || defined(__x86_64__)
198 #    define INPOUTDLLNAME "winio64.dll"
199 #  else
200 #    define INPOUTDLLNAME "winio32.dll"
201 #    define INPOUTDLLOLDNAME "winio.dll"
202 #  endif
203 #endif
204 
detect_sid_uno(void)205 static int detect_sid_uno(void)
206 {
207     int i;
208     int j;
209 
210     for (j = 0; j < 4; ++j) {
211         for (i = 0x18; i >= 0; --i) {
212             hs_pci_store((WORD)i, 0, j);
213         }
214     }
215 
216     hs_pci_store(0x12, 0xff, 0);
217 
218     for (i = 0; i < 100; ++i) {
219         if (hs_pci_read(0x1b, 3)) {
220             return 0;
221         }
222     }
223 
224     hs_pci_store(0x0e, 0xff, 0);
225     hs_pci_store(0x0f, 0xff, 0);
226     hs_pci_store(0x12, 0x20, 0);
227 
228     for (i = 0; i < 100; ++i) {
229         if (hs_pci_read(0x1b, 3)) {
230             return 1;
231         }
232     }
233     return 0;
234 }
235 
detect_sid(int chipno)236 static int detect_sid(int chipno)
237 {
238     int i;
239 
240     for (i = 0x18; i >= 0; --i) {
241         hs_pci_store((WORD)i, 0, chipno);
242     }
243 
244     hs_pci_store(0x12, 0xff, chipno);
245 
246     for (i = 0; i < 100; ++i) {
247         if (hs_pci_read(0x1b, chipno)) {
248             return 0;
249         }
250     }
251 
252     hs_pci_store(0x0e, 0xff, chipno);
253     hs_pci_store(0x0f, 0xff, chipno);
254     hs_pci_store(0x12, 0x20, chipno);
255 
256     for (i = 0; i < 100; ++i) {
257         if (hs_pci_read(0x1b, chipno)) {
258             return 1;
259         }
260     }
261     return 0;
262 }
263 
264 #ifndef KEY_WOW64_64KEY
265 #define KEY_WOW64_64KEY 0x0100
266 #endif
267 
268 #ifndef KEY_WOW64_32KEY
269 #define KEY_WOW64_32KEY 0x0200
270 #endif
271 
272 /* RegOpenKeyEx wrapper for smart access to both 32bit and 64bit registry entries */
RegOpenKeyEx3264(HKEY hKey,LPCTSTR lpSubKey,DWORD ulOptions,REGSAM samDesired,PHKEY phkResult)273 static LONG RegOpenKeyEx3264(HKEY hKey, LPCTSTR lpSubKey, DWORD ulOptions, REGSAM samDesired, PHKEY phkResult)
274 {
275     LONG retval = 0;
276 
277     /* Check 64bit first */
278     retval = RegOpenKeyEx(hKey, lpSubKey, ulOptions, samDesired | KEY_WOW64_64KEY, phkResult);
279 
280     if (retval == ERROR_SUCCESS) {
281         return retval;
282     }
283 
284     /* Check 32bit second */
285     retval = RegOpenKeyEx(hKey, lpSubKey, ulOptions, samDesired | KEY_WOW64_32KEY, phkResult);
286 
287     if (retval == ERROR_SUCCESS) {
288         return retval;
289     }
290 
291     /* Fallback to normal open */
292     retval = RegOpenKeyEx(hKey, lpSubKey, ulOptions, samDesired, phkResult);
293 
294     return retval;
295 }
296 
has_pci(void)297 static int has_pci(void)
298 {
299     HKEY hKey;
300     LONG ret;
301 
302     if (is_windows_nt()) {
303         return 1;
304     }
305 
306     ret = RegOpenKeyEx3264(HKEY_LOCAL_MACHINE, "Enum\\PCI", 0, KEY_QUERY_VALUE, &hKey);
307     if (ret == ERROR_SUCCESS) {
308         RegCloseKey(hKey);
309         return 1;
310     }
311 
312     ret = RegOpenKeyEx3264(HKEY_LOCAL_MACHINE, "SYSTEM\\CurrentControlSet\\Enum\\PCI", 0, KEY_QUERY_VALUE, &hKey);
313     if (ret == ERROR_SUCCESS) {
314         RegCloseKey(hKey);
315         return 1;
316     }
317 
318     return 0;
319 }
320 
find_pci_device(int vendorID,int deviceID)321 static int find_pci_device(int vendorID, int deviceID)
322 {
323     int bus_index;
324     int slot_index;
325     int func_index;
326     unsigned int address;
327     unsigned int device;
328 
329     for (bus_index = 0; bus_index < 256; ++bus_index) {
330         for (slot_index = 0; slot_index < 32; ++slot_index) {
331             for (func_index = 0; func_index < 8; ++func_index) {
332                 address = 0x80000000 | (bus_index << 16) | (slot_index << 11) | (func_index << 8);
333                 hardsid_outl(0xCF8, address);
334                 device = hardsid_inl(0xCFC);
335                 if (device == (unsigned int)(vendorID | (deviceID << 16))) {
336                     address |= 0x10;
337                     hardsid_outl(0xCF8, address);
338                     io1 = hardsid_inl(0xCFC) & 0xFFFC;
339                     address |= 0x04;
340                     hardsid_outl(0xCF8, address);
341                     io2 = hardsid_inl(0xCFC) & 0xFFFC;
342                     return 0;
343                 }
344             }
345         }
346     }
347     return -1;
348 }
349 
close_device(void)350 static void close_device(void)
351 {
352     if (hardsid_use_lib) {
353         shutdown32fp();
354         FreeLibrary(hLib);
355         hLib = NULL;
356     }
357 }
358 
hs_pci_open(void)359 int hs_pci_open(void)
360 {
361     int i;
362     int res;
363     char *openedlib = NULL;
364 
365     if (!sids_found) {
366         return -1;
367     }
368 
369     if (sids_found > 0) {
370         return 0;
371     }
372 
373     sids_found = 0;
374 
375     log_message(LOG_DEFAULT, "Detecting PCI HardSID boards.");
376 
377     if (!has_pci()) {
378         log_message(LOG_DEFAULT, "No PCI bus present.");
379         return -1;
380     }
381 
382     hardsid_use_lib = 0;
383 
384     /* Only use dll when on win nt and up */
385     if (!(GetVersion() & 0x80000000) && hardsid_use_lib == 0) {
386 
387 #ifdef INPOUTDLLOLDNAME
388         if (hLib == NULL) {
389             openedlib = INPOUTDLLOLDNAME;
390             hLib = LoadLibrary(INPOUTDLLOLDNAME);
391         }
392 #endif
393 
394         if (hLib == NULL) {
395             hLib = LoadLibrary(INPOUTDLLNAME);
396             openedlib = INPOUTDLLNAME;
397         }
398 
399         if (hLib != NULL) {
400             log_message(LOG_DEFAULT, "Opened %s.", openedlib);
401 
402             inp32fp = (inpfuncPtr)GetProcAddress(hLib, "GetPortVal");
403             if (inp32fp != NULL) {
404                 oup32fp = (oupfuncPtr)GetProcAddress(hLib, "SetPortVal");
405                 if (oup32fp != NULL) {
406                     init32fp = (initfuncPtr)GetProcAddress(hLib, "InitializeWinIo");
407                     if (init32fp != NULL) {
408                         shutdown32fp = (shutdownfuncPtr)GetProcAddress(hLib, "ShutdownWinIo");
409                         if (shutdown32fp != NULL) {
410                             if (init32fp()) {
411                                 hardsid_use_lib = 1;
412                                 log_message(LOG_DEFAULT, "Using %s for PCI I/O access.", openedlib);
413                             } else {
414                                 log_message(LOG_DEFAULT, "Cannot init %s.", openedlib);
415                             }
416                         } else {
417                             log_message(LOG_DEFAULT, "Cannot get 'ShutdownWinIo' function from %s.", openedlib);
418                         }
419                     } else {
420                         log_message(LOG_DEFAULT, "Cannot get 'InitializeWinIo' function from %s.", openedlib);
421                     }
422                 } else {
423                     log_message(LOG_DEFAULT, "Cannot get 'SetPortVal' function from %s.", openedlib);
424                 }
425             } else {
426                 log_message(LOG_DEFAULT, "Cannot get 'GetPortVal' function from %s.", openedlib);
427             }
428             if (!hardsid_use_lib) {
429                 log_message(LOG_DEFAULT, "Cannot get I/O functions in %s, using direct PCI I/O access.", openedlib);
430             }
431         } else {
432             log_message(LOG_DEFAULT, "Cannot open %s, trying direct PCI I/O access.", openedlib);
433         }
434     } else {
435         log_message(LOG_DEFAULT, "Using direct PCI I/O access.");
436     }
437 
438     if (!(GetVersion() & 0x80000000) && hardsid_use_lib == 0) {
439         log_message(LOG_DEFAULT, "Cannot use direct PCI I/O access on Windows NT/2000/Server/XP/Vista/7/8/10.");
440         return -1;
441     }
442 
443     res = find_pci_device(0x6581, 0x8580);
444 
445     if (res < 0) {
446         log_message(LOG_DEFAULT, "No PCI HardSID found.");
447         close_device();
448         return -1;
449     }
450 
451     log_message(LOG_DEFAULT, "PCI HardSID board found at $%04X and $%04X", io1, io2);
452 
453     for (i = 0; i < MAXSID; ++i) {
454         hssids[sids_found] = i;
455         if (detect_sid(i)) {
456             sids_found++;
457         }
458     }
459 
460     if (!sids_found) {
461         log_message(LOG_DEFAULT, "No PCI HardSID found.");
462         close_device();
463         return -1;
464     }
465 
466     /* Check for classic HardSID if 4 SIDs were found. */
467     if (sids_found == 4) {
468         if (detect_sid_uno()) {
469             sids_found = 1;
470         }
471     }
472 
473     log_message(LOG_DEFAULT, "PCI HardSID: opened, found %d SIDs.", sids_found);
474 
475     return 0;
476 }
477 
hs_pci_close(void)478 int hs_pci_close(void)
479 {
480     int i;
481 
482     close_device();
483 
484     for (i = 0; i < MAXSID; ++i) {
485         if (hssids[i] != -1) {
486             hssids[i] = -1;
487         }
488     }
489 
490     sids_found = -1;
491 
492     log_message(LOG_DEFAULT, "PCI HardSID: closed");
493 
494     return 0;
495 }
496 
hs_pci_available(void)497 int hs_pci_available(void)
498 {
499     return sids_found;
500 }
501 
502 /* ---------------------------------------------------------------------*/
503 
hs_pci_state_read(int chipno,struct sid_hs_snapshot_state_s * sid_state)504 void hs_pci_state_read(int chipno, struct sid_hs_snapshot_state_s *sid_state)
505 {
506     sid_state->hsid_main_clk = 0;
507     sid_state->hsid_alarm_clk = 0;
508     sid_state->lastaccess_clk = 0;
509     sid_state->lastaccess_ms = 0;
510     sid_state->lastaccess_chipno = 0;
511     sid_state->chipused = 0;
512     sid_state->device_map[0] = 0;
513     sid_state->device_map[1] = 0;
514     sid_state->device_map[2] = 0;
515     sid_state->device_map[3] = 0;
516 }
517 
hs_pci_state_write(int chipno,struct sid_hs_snapshot_state_s * sid_state)518 void hs_pci_state_write(int chipno, struct sid_hs_snapshot_state_s *sid_state)
519 {
520 }
521 #endif
522 #endif
523 
524