1 /** \file   cw-win32-pci.c
2  * \brief   Windows specific PCI cw3 driver
3  *
4  * \author  Marco van den Heuvel <blackystardust68@yahoo.com>
5  *
6  * This file is part of VICE, the Versatile Commodore Emulator.
7  * See README for copyright notice.
8  *
9  *  This program is free software; you can redistribute it and/or modify
10  *  it under the terms of the GNU General Public License as published by
11  *  the Free Software Foundation; either version 2 of the License, or
12  *  (at your option) any later version.
13  *
14  *  This program is distributed in the hope that it will be useful,
15  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
16  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  *  GNU General Public License for more details.
18  *
19  *  You should have received a copy of the GNU General Public License
20  *  along with this program; if not, write to the Free Software
21  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
22  *  02111-1307  USA.
23  *
24  */
25 
26 /* Tested and confirmed working on:
27 
28  - Windows 95C (Direct PCI I/O)
29  - Windows 98SE (Direct PCI I/O)
30  - Windows ME (Direct PCI I/O)
31  - Windows NT 4 (winio32.dll PCI I/O)
32  - Windows 2000 (winio32.dll PCI I/O)
33  - Windows XP (winio32.dll PCI I/O)
34  */
35 
36 #include "vice.h"
37 
38 #ifdef WIN32_COMPILE
39 
40 #ifdef HAVE_CATWEASELMKIII
41 #include <windows.h>
42 #include <fcntl.h>
43 #include <stdio.h>
44 #include <assert.h>
45 
46 #ifdef HAVE_UNISTD_H
47 #include <unistd.h>
48 #endif
49 
50 #include "alarm.h"
51 #include "archdep.h"
52 #include "catweaselmkiii.h"
53 #include "cw-win32.h"
54 #include "log.h"
55 #include "sid-resources.h"
56 #include "types.h"
57 #include "wininpoutp.h"
58 
59 #define CW_SID_DAT 0xd8
60 #define CW_SID_CMD 0xdc
61 
62 #define MAXSID 1
63 
64 static int sids_found = -1;
65 static int base = -1;
66 
67 static int cw_use_lib = 0;
68 
69 #ifndef MSVC_RC
70 typedef int _stdcall (*initfuncPtr)(void);
71 typedef void _stdcall (*shutdownfuncPtr)(void);
72 typedef int _stdcall (*inpfuncPtr)(WORD port, PDWORD value, BYTE size);
73 typedef int _stdcall (*oupfuncPtr)(WORD port, DWORD value, BYTE size);
74 #else
75 typedef int (CALLBACK* initfuncPtr)(void);
76 typedef void (CALLBACK* shutdownfuncPtr)(void);
77 typedef int (CALLBACK* inpfuncPtr)(WORD, PDWORD, BYTE);
78 typedef int (CALLBACK* oupfuncPtr)(WORD, DWORD, BYTE);
79 #endif
80 
81 static initfuncPtr init32fp;
82 static shutdownfuncPtr shutdown32fp;
83 static inpfuncPtr inp32fp;
84 static oupfuncPtr oup32fp;
85 
86 
87 /* input/output functions */
cw_outb(unsigned int addrint,DWORD value)88 static void cw_outb(unsigned int addrint, DWORD value)
89 {
90 #ifdef  _M_IX86
91     _outp(addrint, (BYTE)value);
92 #else
93     (void)addrint;
94 #endif
95 }
96 
cw_outl(unsigned int addrint,DWORD value)97 static void cw_outl(unsigned int addrint, DWORD value)
98 {
99 #ifdef  _M_IX86
100     _outpd(addrint, value);
101 #else
102     (void)addrint;
103 #endif
104 }
105 
cw_inb(unsigned int addrint)106 static BYTE cw_inb(unsigned int addrint)
107 {
108 #ifdef  _M_IX86
109     return _inp(addrint);
110 #else
111     (void)addrint;
112     return 0;
113 #endif
114 }
115 
cw_inl(unsigned int addrint)116 static DWORD cw_inl(unsigned int addrint)
117 {
118 #ifdef  _M_IX86
119     return _inpd(addrint);
120 #else
121     (void)addrint;
122 #endif
123     return 0;
124 }
125 
cw_pci_read(uint16_t addr,int chipno)126 int cw_pci_read(uint16_t addr, int chipno)
127 {
128     unsigned char cmd;
129 
130     if (chipno < MAXSID && base != -1 && addr < 0x20) {
131         cmd = (addr & 0x1f) | 0x20;
132         if (catweaselmkiii_get_ntsc()) {
133             cmd |= 0x40;
134         }
135         cw_outb(base + CW_SID_CMD, cmd);
136         archdep_usleep(1);
137         return cw_inb(base + CW_SID_DAT);
138     }
139     return 0;
140 }
141 
cw_pci_store(uint16_t addr,uint8_t outval,int chipno)142 void cw_pci_store(uint16_t addr, uint8_t outval, int chipno)
143 {
144     unsigned char cmd;
145 
146     if (chipno < MAXSID && base != -1 && addr < 0x20) {
147         cmd = addr & 0x1f;
148         if (catweaselmkiii_get_ntsc()) {
149             cmd |= 0x40;
150         }
151         cw_outb(base + CW_SID_DAT, outval);
152         cw_outb(base + CW_SID_CMD, cmd);
153         archdep_usleep(1);
154     }
155 }
156 
157 /*----------------------------------------------------------------------*/
158 
159 static HINSTANCE hLib = NULL;
160 
161 /*
162  * Is this shit required? Doesn't MSYS2 take care of this?
163  */
164 #if defined(__amd64__) || defined(__x86_64__)
165 #  define INPOUTDLLNAME "winio64.dll"
166 #else
167 #  define INPOUTDLLNAME "winio32.dll"
168 #  define INPOUTDLLOLDNAME "winio.dll"
169 #endif
170 
detect_sid(void)171 static int detect_sid(void)
172 {
173     int i;
174 
175     for (i = 0x18; i >= 0; --i) {
176         cw_pci_store((WORD)i, 0, 0);
177     }
178 
179     cw_pci_store(0x12, 0xff, 0);
180 
181     for (i = 0; i < 100; ++i) {
182         if (cw_pci_read(0x1b, 0)) {
183             return 0;
184         }
185     }
186 
187     cw_pci_store(0x0e, 0xff, 0);
188     cw_pci_store(0x0f, 0xff, 0);
189     cw_pci_store(0x12, 0x20, 0);
190 
191     for (i = 0; i < 100; ++i) {
192         if (cw_pci_read(0x1b, 0)) {
193             return 1;
194         }
195     }
196     return 0;
197 }
198 
199 #ifndef KEY_WOW64_64KEY
200 #define KEY_WOW64_64KEY 0x0100
201 #endif
202 
203 #ifndef KEY_WOW64_32KEY
204 #define KEY_WOW64_32KEY 0x0200
205 #endif
206 
207 /* RegOpenKeyEx wrapper for smart access to both 32bit and 64bit registry entries */
RegOpenKeyEx3264(HKEY hKey,LPCTSTR lpSubKey,DWORD ulOptions,REGSAM samDesired,PHKEY phkResult)208 static LONG RegOpenKeyEx3264(HKEY hKey, LPCTSTR lpSubKey, DWORD ulOptions, REGSAM samDesired, PHKEY phkResult)
209 {
210     LONG retval = 0;
211 
212     /* Check 64bit first */
213     retval = RegOpenKeyEx(hKey, lpSubKey, ulOptions, samDesired | KEY_WOW64_64KEY, phkResult);
214 
215     if (retval == ERROR_SUCCESS) {
216         return retval;
217     }
218 
219     /* Check 32bit second */
220     retval = RegOpenKeyEx(hKey, lpSubKey, ulOptions, samDesired | KEY_WOW64_32KEY, phkResult);
221 
222     if (retval == ERROR_SUCCESS) {
223         return retval;
224     }
225 
226     /* Fallback to normal open */
227     retval = RegOpenKeyEx(hKey, lpSubKey, ulOptions, samDesired, phkResult);
228 
229     return retval;
230 }
231 
has_pci(void)232 static int has_pci(void)
233 {
234     HKEY hKey;
235     LONG ret;
236 
237     if (archdep_is_windows_nt()) {
238         return 1;
239     }
240 
241     ret = RegOpenKeyEx3264(HKEY_LOCAL_MACHINE, "Enum\\PCI", 0, KEY_QUERY_VALUE, &hKey);
242     if (ret == ERROR_SUCCESS) {
243         RegCloseKey(hKey);
244         return 1;
245     }
246 
247     ret = RegOpenKeyEx3264(HKEY_LOCAL_MACHINE, "SYSTEM\\CurrentControlSet\\Enum\\PCI", 0, KEY_QUERY_VALUE, &hKey);
248     if (ret == ERROR_SUCCESS) {
249         RegCloseKey(hKey);
250         return 1;
251     }
252 
253     return 0;
254 }
255 
find_pci_device(int vendorID,int deviceID)256 static int find_pci_device(int vendorID, int deviceID)
257 {
258     int bus_index;
259     int slot_index;
260     int func_index;
261     unsigned int address;
262     unsigned int device;
263 
264     for (bus_index = 0; bus_index < 256; ++bus_index) {
265         for (slot_index = 0; slot_index < 32; ++slot_index) {
266             for (func_index = 0; func_index < 8; ++func_index) {
267                 address = 0x80000000 | (bus_index << 16) | (slot_index << 11) | (func_index << 8);
268                 cw_outl(0xCF8, address);
269                 device = cw_inl(0xCFC);
270                 if (device == (unsigned int)(vendorID | (deviceID << 16))) {
271                     address |= 0x10;
272                     cw_outl(0xCF8, address);
273                     base = cw_inl(0xCFC) & 0xFFFC;
274                     return 0;
275                 }
276             }
277         }
278     }
279     return -1;
280 }
281 
close_device(void)282 static void close_device(void)
283 {
284     if (cw_use_lib) {
285         shutdown32fp();
286         FreeLibrary(hLib);
287         hLib = NULL;
288     }
289 }
290 
cw_pci_open(void)291 int cw_pci_open(void)
292 {
293     int res;
294     char *openedlib = NULL;
295 
296     if (!sids_found) {
297         return -1;
298     }
299 
300     if (sids_found > 0) {
301         return 0;
302     }
303 
304     sids_found = 0;
305 
306     log_message(LOG_DEFAULT, "Detecting PCI CatWeasel boards.");
307 
308     if (!has_pci()) {
309         log_message(LOG_DEFAULT, "No PCI bus present.");
310         return -1;
311     }
312 
313     /* Only use dll when on win nt and up */
314     if (!(GetVersion() & 0x80000000) && cw_use_lib == 0) {
315 
316 #ifdef INPOUTDLLOLDNAME
317         if (hLib == NULL) {
318             openedlib = INPOUTDLLOLDNAME;
319             hLib = LoadLibrary(INPOUTDLLOLDNAME);
320         }
321 #endif
322 
323         if (hLib == NULL) {
324             hLib = LoadLibrary(INPOUTDLLNAME);
325             openedlib = INPOUTDLLNAME;
326         }
327     }
328 
329     cw_use_lib = 0;
330 
331     if (hLib != NULL) {
332         log_message(LOG_DEFAULT, "Opened %s.", openedlib);
333 
334         inp32fp = (inpfuncPtr)GetProcAddress(hLib, "GetPortVal");
335         if (inp32fp != NULL) {
336             oup32fp = (oupfuncPtr)GetProcAddress(hLib, "SetPortVal");
337             if (oup32fp != NULL) {
338                 init32fp = (initfuncPtr)GetProcAddress(hLib, "InitializeWinIo");
339                 if (init32fp != NULL) {
340                     shutdown32fp = (shutdownfuncPtr)GetProcAddress(hLib, "ShutdownWinIo");
341                     if (shutdown32fp != NULL) {
342                         if (init32fp()) {
343                             cw_use_lib = 1;
344                             log_message(LOG_DEFAULT, "Using %s for PCI I/O access.", openedlib);
345                         } else {
346                             log_message(LOG_DEFAULT, "init call failed in %s.", openedlib);
347                         }
348                     } else {
349                         log_message(LOG_DEFAULT, "Cannot get 'ShutdownWinIo' function from %s.", openedlib);
350                     }
351                 } else {
352                     log_message(LOG_DEFAULT, "Cannot get 'InitializeWinIo' function from %s.", openedlib);
353                 }
354             } else {
355                 log_message(LOG_DEFAULT, "Cannot get 'SetPortVal' function from %s.", openedlib);
356             }
357         } else {
358             log_message(LOG_DEFAULT, "Cannot get 'GetPortVal' function from %s.", openedlib);
359         }
360         if (!cw_use_lib) {
361             log_message(LOG_DEFAULT, "Cannot get I/O functions in %s, using direct PCI I/O access.", openedlib);
362         }
363     } else {
364         log_message(LOG_DEFAULT, "Cannot open %s, trying direct PCI I/O access.", openedlib);
365     }
366 
367 
368     if (!(GetVersion() & 0x80000000) && cw_use_lib == 0) {
369         log_message(LOG_DEFAULT, "Cannot use direct PCI I/O access on Windows NT/2000/Server/XP/Vista/7/8/10.");
370         return -1;
371     }
372 
373     res = find_pci_device(0xe159, 0x0001);
374 
375     if (res < 0) {
376         log_message(LOG_DEFAULT, "No PCI CatWeasel found.");
377         close_device();
378         return -1;
379     }
380 
381     log_message(LOG_DEFAULT, "PCI CatWeasel board found at $%04X.",
382             (unsigned int)base);
383 
384     if (detect_sid()) {
385         sids_found++;
386     }
387 
388     if (!sids_found) {
389         log_message(LOG_DEFAULT, "No PCI CatWeasel found.");
390         close_device();
391         return -1;
392     }
393 
394     log_message(LOG_DEFAULT, "PCI CatWeasel: opened, found %d SIDs.", sids_found);
395 
396     return 0;
397 }
398 
cw_pci_close(void)399 int cw_pci_close(void)
400 {
401     close_device();
402 
403     base = -1;
404 
405     sids_found = -1;
406 
407     log_message(LOG_DEFAULT, "PCI CatWeasel: closed");
408 
409     return 0;
410 }
411 
cw_pci_available(void)412 int cw_pci_available(void)
413 {
414     return sids_found;
415 }
416 #endif
417 #endif
418