1 /*
2  * cw-win32-pci.c - Windows specific PCI cw3 driver.
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 (Direct PCI I/O)
30  - Windows 98SE (Direct PCI I/O)
31  - Windows ME (Direct PCI I/O)
32  - Windows NT 4 (winio32.dll PCI I/O)
33  - Windows 2000 (winio32.dll PCI I/O)
34  - Windows XP (winio32.dll PCI I/O)
35  */
36 
37 #include "vice.h"
38 
39 #ifdef WIN32_COMPILE
40 
41 #ifdef HAVE_CATWEASELMKIII
42 #include <windows.h>
43 #include <fcntl.h>
44 #include <stdio.h>
45 #include <assert.h>
46 
47 #ifdef HAVE_UNISTD_H
48 #include <unistd.h>
49 #endif
50 
51 #include "alarm.h"
52 #include "archdep.h"
53 #include "catweaselmkiii.h"
54 #include "cw-win32.h"
55 #include "log.h"
56 #include "sid-resources.h"
57 #include "types.h"
58 #include "wininpoutp.h"
59 
60 #define CW_SID_DAT 0xd8
61 #define CW_SID_CMD 0xdc
62 
63 #define MAXSID 1
64 
65 static int sids_found = -1;
66 static int base = -1;
67 
68 static int cw_use_lib = 0;
69 
70 #ifndef MSVC_RC
71 typedef int _stdcall (*initfuncPtr)(void);
72 typedef void _stdcall (*shutdownfuncPtr)(void);
73 typedef int _stdcall (*inpfuncPtr)(WORD port, PDWORD value, BYTE size);
74 typedef int _stdcall (*oupfuncPtr)(WORD port, DWORD value, BYTE size);
75 #else
76 typedef int (CALLBACK* initfuncPtr)(void);
77 typedef void (CALLBACK* shutdownfuncPtr)(void);
78 typedef int (CALLBACK* inpfuncPtr)(WORD, PDWORD, BYTE);
79 typedef int (CALLBACK* oupfuncPtr)(WORD, DWORD, BYTE);
80 #endif
81 
82 static initfuncPtr init32fp;
83 static shutdownfuncPtr shutdown32fp;
84 static inpfuncPtr inp32fp;
85 static oupfuncPtr oup32fp;
86 
87 /* input/output functions */
cw_outb(unsigned int addrint,DWORD value)88 static void cw_outb(unsigned int addrint, DWORD value)
89 {
90     WORD addr = (WORD)addrint;
91 
92     /* make sure the above conversion did not loose any details */
93     assert(addr == addrint);
94 
95 #ifdef  _M_IX86
96 #ifdef WATCOM_COMPILE
97     outp(addr, (BYTE)value);
98 #else
99     _outp(addr, (BYTE)value);
100 #endif
101 #endif
102 }
103 
cw_outl(unsigned int addrint,DWORD value)104 static void cw_outl(unsigned int addrint, DWORD value)
105 {
106     WORD addr = (WORD)addrint;
107 
108     /* make sure the above conversion did not loose any details */
109     assert(addr == addrint);
110 
111 #ifdef  _M_IX86
112 #ifdef WATCOM_COMPILE
113     outpd(addr, value);
114 #else
115     _outpd(addr, value);
116 #endif
117 #endif
118 }
119 
cw_inb(unsigned int addrint)120 static BYTE cw_inb(unsigned int addrint)
121 {
122     WORD addr = (WORD)addrint;
123 
124     /* make sure the above conversion did not loose any details */
125     assert(addr == addrint);
126 
127 #ifdef  _M_IX86
128 #ifdef WATCOM_COMPILE
129     return inp(addr);
130 #else
131     return _inp(addr);
132 #endif
133 #endif
134     return 0;
135 }
136 
cw_inl(unsigned int addrint)137 static DWORD cw_inl(unsigned int addrint)
138 {
139     WORD addr = (WORD)addrint;
140 
141     /* make sure the above conversion did not loose any details */
142     assert(addr == addrint);
143 
144 #ifdef  _M_IX86
145 #ifdef WATCOM_COMPILE
146     return inpd(addr);
147 #else
148     return _inpd(addr);
149 #endif
150 #endif
151     return 0;
152 }
153 
cw_pci_read(uint16_t addr,int chipno)154 int cw_pci_read(uint16_t addr, int chipno)
155 {
156     unsigned char cmd;
157 
158     if (chipno < MAXSID && base != -1 && addr < 0x20) {
159         cmd = (addr & 0x1f) | 0x20;
160         if (catweaselmkiii_get_ntsc()) {
161             cmd |= 0x40;
162         }
163         cw_outb(base + CW_SID_CMD, cmd);
164         vice_usleep(1);
165         return cw_inb(base + CW_SID_DAT);
166     }
167     return 0;
168 }
169 
cw_pci_store(uint16_t addr,uint8_t outval,int chipno)170 void cw_pci_store(uint16_t addr, uint8_t outval, int chipno)
171 {
172     unsigned char cmd;
173 
174     if (chipno < MAXSID && base != -1 && addr < 0x20) {
175         cmd = addr & 0x1f;
176         if (catweaselmkiii_get_ntsc()) {
177             cmd |= 0x40;
178         }
179         cw_outb(base + CW_SID_DAT, outval);
180         cw_outb(base + CW_SID_CMD, cmd);
181         vice_usleep(1);
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(void)205 static int detect_sid(void)
206 {
207     int i;
208 
209     for (i = 0x18; i >= 0; --i) {
210         cw_pci_store((WORD)i, 0, 0);
211     }
212 
213     cw_pci_store(0x12, 0xff, 0);
214 
215     for (i = 0; i < 100; ++i) {
216         if (cw_pci_read(0x1b, 0)) {
217             return 0;
218         }
219     }
220 
221     cw_pci_store(0x0e, 0xff, 0);
222     cw_pci_store(0x0f, 0xff, 0);
223     cw_pci_store(0x12, 0x20, 0);
224 
225     for (i = 0; i < 100; ++i) {
226         if (cw_pci_read(0x1b, 0)) {
227             return 1;
228         }
229     }
230     return 0;
231 }
232 
233 #ifndef KEY_WOW64_64KEY
234 #define KEY_WOW64_64KEY 0x0100
235 #endif
236 
237 #ifndef KEY_WOW64_32KEY
238 #define KEY_WOW64_32KEY 0x0200
239 #endif
240 
241 /* RegOpenKeyEx wrapper for smart access to both 32bit and 64bit registry entries */
RegOpenKeyEx3264(HKEY hKey,LPCTSTR lpSubKey,DWORD ulOptions,REGSAM samDesired,PHKEY phkResult)242 static LONG RegOpenKeyEx3264(HKEY hKey, LPCTSTR lpSubKey, DWORD ulOptions, REGSAM samDesired, PHKEY phkResult)
243 {
244     LONG retval = 0;
245 
246     /* Check 64bit first */
247     retval = RegOpenKeyEx(hKey, lpSubKey, ulOptions, samDesired | KEY_WOW64_64KEY, phkResult);
248 
249     if (retval == ERROR_SUCCESS) {
250         return retval;
251     }
252 
253     /* Check 32bit second */
254     retval = RegOpenKeyEx(hKey, lpSubKey, ulOptions, samDesired | KEY_WOW64_32KEY, phkResult);
255 
256     if (retval == ERROR_SUCCESS) {
257         return retval;
258     }
259 
260     /* Fallback to normal open */
261     retval = RegOpenKeyEx(hKey, lpSubKey, ulOptions, samDesired, phkResult);
262 
263     return retval;
264 }
265 
has_pci(void)266 static int has_pci(void)
267 {
268     HKEY hKey;
269     LONG ret;
270 
271     if (is_windows_nt()) {
272         return 1;
273     }
274 
275     ret = RegOpenKeyEx3264(HKEY_LOCAL_MACHINE, "Enum\\PCI", 0, KEY_QUERY_VALUE, &hKey);
276     if (ret == ERROR_SUCCESS) {
277         RegCloseKey(hKey);
278         return 1;
279     }
280 
281     ret = RegOpenKeyEx3264(HKEY_LOCAL_MACHINE, "SYSTEM\\CurrentControlSet\\Enum\\PCI", 0, KEY_QUERY_VALUE, &hKey);
282     if (ret == ERROR_SUCCESS) {
283         RegCloseKey(hKey);
284         return 1;
285     }
286 
287     return 0;
288 }
289 
find_pci_device(int vendorID,int deviceID)290 static int find_pci_device(int vendorID, int deviceID)
291 {
292     int bus_index;
293     int slot_index;
294     int func_index;
295     unsigned int address;
296     unsigned int device;
297 
298     for (bus_index = 0; bus_index < 256; ++bus_index) {
299         for (slot_index = 0; slot_index < 32; ++slot_index) {
300             for (func_index = 0; func_index < 8; ++func_index) {
301                 address = 0x80000000 | (bus_index << 16) | (slot_index << 11) | (func_index << 8);
302                 cw_outl(0xCF8, address);
303                 device = cw_inl(0xCFC);
304                 if (device == (unsigned int)(vendorID | (deviceID << 16))) {
305                     address |= 0x10;
306                     cw_outl(0xCF8, address);
307                     base = cw_inl(0xCFC) & 0xFFFC;
308                     return 0;
309                 }
310             }
311         }
312     }
313     return -1;
314 }
315 
close_device(void)316 static void close_device(void)
317 {
318     if (cw_use_lib) {
319         shutdown32fp();
320         FreeLibrary(hLib);
321         hLib = NULL;
322     }
323 }
324 
cw_pci_open(void)325 int cw_pci_open(void)
326 {
327     int res;
328     char *openedlib = NULL;
329 
330     if (!sids_found) {
331         return -1;
332     }
333 
334     if (sids_found > 0) {
335         return 0;
336     }
337 
338     sids_found = 0;
339 
340     log_message(LOG_DEFAULT, "Detecting PCI CatWeasel boards.");
341 
342     if (!has_pci()) {
343         log_message(LOG_DEFAULT, "No PCI bus present.");
344         return -1;
345     }
346 
347     /* Only use dll when on win nt and up */
348     if (!(GetVersion() & 0x80000000) && cw_use_lib == 0) {
349 
350 #ifdef INPOUTDLLOLDNAME
351         if (hLib == NULL) {
352             openedlib = INPOUTDLLOLDNAME;
353             hLib = LoadLibrary(INPOUTDLLOLDNAME);
354         }
355 #endif
356 
357         if (hLib == NULL) {
358             hLib = LoadLibrary(INPOUTDLLNAME);
359             openedlib = INPOUTDLLNAME;
360         }
361     }
362 
363     cw_use_lib = 0;
364 
365     if (hLib != NULL) {
366         log_message(LOG_DEFAULT, "Opened %s.", openedlib);
367 
368         inp32fp = (inpfuncPtr)GetProcAddress(hLib, "GetPortVal");
369         if (inp32fp != NULL) {
370             oup32fp = (oupfuncPtr)GetProcAddress(hLib, "SetPortVal");
371             if (oup32fp != NULL) {
372                 init32fp = (initfuncPtr)GetProcAddress(hLib, "InitializeWinIo");
373                 if (init32fp != NULL) {
374                     shutdown32fp = (shutdownfuncPtr)GetProcAddress(hLib, "ShutdownWinIo");
375                     if (shutdown32fp != NULL) {
376                         if (init32fp()) {
377                             cw_use_lib = 1;
378                             log_message(LOG_DEFAULT, "Using %s for PCI I/O access.", openedlib);
379                         } else {
380                             log_message(LOG_DEFAULT, "init call failed in %s.", openedlib);
381                         }
382                     } else {
383                         log_message(LOG_DEFAULT, "Cannot get 'ShutdownWinIo' function from %s.", openedlib);
384                     }
385                 } else {
386                     log_message(LOG_DEFAULT, "Cannot get 'InitializeWinIo' function from %s.", openedlib);
387                 }
388             } else {
389                 log_message(LOG_DEFAULT, "Cannot get 'SetPortVal' function from %s.", openedlib);
390             }
391         } else {
392             log_message(LOG_DEFAULT, "Cannot get 'GetPortVal' function from %s.", openedlib);
393         }
394         if (!cw_use_lib) {
395             log_message(LOG_DEFAULT, "Cannot get I/O functions in %s, using direct PCI I/O access.", openedlib);
396         }
397     } else {
398         log_message(LOG_DEFAULT, "Cannot open %s, trying direct PCI I/O access.", openedlib);
399     }
400 
401 
402     if (!(GetVersion() & 0x80000000) && cw_use_lib == 0) {
403         log_message(LOG_DEFAULT, "Cannot use direct PCI I/O access on Windows NT/2000/Server/XP/Vista/7/8/10.");
404         return -1;
405     }
406 
407     res = find_pci_device(0xe159, 0x0001);
408 
409     if (res < 0) {
410         log_message(LOG_DEFAULT, "No PCI CatWeasel found.");
411         close_device();
412         return -1;
413     }
414 
415     log_message(LOG_DEFAULT, "PCI CatWeasel board found at $%04X.", base);
416 
417     if (detect_sid()) {
418         sids_found++;
419     }
420 
421     if (!sids_found) {
422         log_message(LOG_DEFAULT, "No PCI CatWeasel found.");
423         close_device();
424         return -1;
425     }
426 
427     log_message(LOG_DEFAULT, "PCI CatWeasel: opened, found %d SIDs.", sids_found);
428 
429     return 0;
430 }
431 
cw_pci_close(void)432 int cw_pci_close(void)
433 {
434     close_device();
435 
436     base = -1;
437 
438     sids_found = -1;
439 
440     log_message(LOG_DEFAULT, "PCI CatWeasel: closed");
441 
442     return 0;
443 }
444 
cw_pci_available(void)445 int cw_pci_available(void)
446 {
447     return sids_found;
448 }
449 #endif
450 #endif
451