summaryrefslogtreecommitdiffstats
path: root/private/mvdm/vdd/samples/adlibvdd/vdd.c
blob: 7a027ebb4ca71c3c474c21edce7470c030956c98 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
/****************************************************************************
 *
 *   config.c
 *
 *   Copyright (c) 1991 Microsoft Corporation.  All Rights Reserved.
 *
 ***************************************************************************/

/*
 *  Definition of interface to kernel driver (synth.sys)
 *
 *     The kernel driver's Dos device name is assumed fixed and known
 *
 *          adlib.mid or adlib.mid0
 *
 *     The kernel driver is opened in read/write mode.
 *
 *     Writing to the driver sends a list of SYNTH_DATA structures
 *     to the driver.  The port number MUST be 0x388 or 0x389.
 *
 *
 *     Reading always reads just 1 byte - the status port.
 */

#include <windows.h>              // The VDD is just a win32 DLL
#include <vddsvc.h>               // Definition of VDD calls
#include "vdd.h"                // Common data with kernel driver
#include <stdio.h>

/*
 *  Debugging
 */

#if DBG

    int VddDebugLevel = 1;


   /***************************************************************************

    Generate debug output in printf type format

    ****************************************************************************/

    void VddDbgOut(LPSTR lpszFormat, ...)
    {
        char buf[256];
        va_list va;

        OutputDebugStringA("Ad Lib VDD: ");

        va_start(va, lpszFormat);
        vsprintf(buf, lpszFormat, va);
        va_end(va);

        OutputDebugStringA(buf);
        OutputDebugStringA("\r\n");
    }

    #define dprintf( _x_ )                          VddDbgOut _x_
    #define dprintf1( _x_ ) if (VddDebugLevel >= 1) VddDbgOut _x_
    #define dprintf2( _x_ ) if (VddDebugLevel >= 2) VddDbgOut _x_
    #define dprintf3( _x_ ) if (VddDebugLevel >= 3) VddDbgOut _x_
    #define dprintf4( _x_ ) if (VddDebugLevel >= 4) VddDbgOut _x_


#else

    #define dprintf(x)
    #define dprintf1(x)
    #define dprintf2(x)
    #define dprintf3(x)
    #define dprintf4(x)

#endif // DBG


/*
 *   Symbolic names for port addresses
 */

 #define ADLIB_DATA_PORT 0x389
 #define ADLIB_REGISTER_SELECT_PORT 0x388
 #define ADLIB_STATUS_PORT 0x388

/*
 *   Batch data to the device - for true Adlib use a size of 2
 */

 #define BATCH_SIZE 40
 int Position = 0;
 SYNTH_DATA PortData[BATCH_SIZE];


/*
 *  Internal Routines
 */

 void MyByteIn(WORD port, BYTE *data);
 void MyByteOut(WORD port, BYTE data);

/*
 *  IO handler table.
 *
 *  There's no point in providing string handlers because the chip
 *  can't respond very quickly (need gaps of at least 23 microseconds
 *  between writes).
 */

 VDD_IO_HANDLERS handlers = {
     MyByteIn,
     NULL,
     NULL,
     NULL,
     MyByteOut,
     NULL,
     NULL,
     NULL};

/*
 *  Note that we rely on the kernel driver to pretend the device is
 *  at address 388 even the driver supports it somewhere else.
 */

 VDD_IO_PORTRANGE ports[] = {
    {
       0x228,
       0x229
    },
    {
       0x388,
       0x389
    }
 };

/*
 *  Globals
 */


 //
 // Track timers.  The basic rule is that if no timer is started then
 // the only way the status register can change is via the reset bit
 // in which case we know what will happen.
 //
 // If a timer interrupts then it's 'stopped'
 //

 BOOL Timer1Started;
 BOOL Timer2Started;
 BYTE Status;

/*
 *  Current device handle
 *
 *  NULL if device is (potentially) free
 *  INVALID_HANDLE_VALUE if device was not obtainable
 */

 HANDLE DeviceHandle;

 HANDLE OpenDevice(PWSTR DeviceName)
 {
     WCHAR DosDeviceName[MAX_PATH];


    /*
     *  Make up a string suitable for opening a Dos device
     */

     wcscpy(DosDeviceName, TEXT("\\\\."));
     wcscat(DosDeviceName, DeviceName +
                           wcslen(TEXT("\\Device")));

    /*
     *  Open the device with GENERIC_READ and GENERIC_WRITE
     *  Also use FILE_SHARE_WRITE so other applications can
     *  set the device volume
     */

     return         CreateFile(DosDeviceName,
                               GENERIC_WRITE | GENERIC_READ,
                               FILE_SHARE_WRITE,
                               NULL,
                               OPEN_EXISTING,
                               0,
                               NULL);

 }

/*
 *  Open our device is it can be opened and we haven't tried before
 *
 *  Returns FALSE if device can't be acquired.
 */

 BOOL CheckDeviceAccess(void)
 {

    /*
     *  If we don't have a handle (valid or invalid) already try
     *  opening the device
     */

     if (DeviceHandle == NULL) {

         DeviceHandle = OpenDevice(STR_ADLIB_DEVICENAME);

         if (DeviceHandle == INVALID_HANDLE_VALUE) {
             DeviceHandle = OpenDevice(STR_ADLIB_DEVICENAME L"0");
         }
         Position = 0;
     }

     return DeviceHandle != INVALID_HANDLE_VALUE;
 }

/*
 *  Map a write to a port
 *
 *  How are we going to simulate timer stuff?
 *  Answer: Allow reading of the status port.
 *
 *  This is optimized to only write when we get a data port write
 */


 void MyByteOut(WORD port, BYTE data)
 {
     //
     // Remember what register is selected
     //

     static BYTE AdlibRegister;

     //
     // Just package the stuff up and call write file
     //

     DWORD BytesWritten;

     dprintf3(("Received write to Port %4X, Data %2X", port, data));

     port = (port & 1) | ADLIB_REGISTER_SELECT_PORT;


    /*
     *  Check for special values - don't let them switch to
     *  OPL3 mode.
     */

#if 0
     if (port == ADLIB_DATA_PORT && AdlibRegister == AD_NEW) {
         data &= 0xFE;
     }
#endif


     if (port == ADLIB_REGISTER_SELECT_PORT) {
        /*
         *  Just remember which register is supposed to be selected
         *  to cut down the number of times we go to the device driver
         */

         AdlibRegister = data;
     } else {

        /*
         *  Write this one to the device
         */

         PortData[Position].IoPort = ADLIB_REGISTER_SELECT_PORT;
         PortData[Position].PortData = AdlibRegister;
         PortData[Position + 1].IoPort = port;
         PortData[Position + 1].PortData = data;

         Position += 2;

         if (Position == BATCH_SIZE ||
             AdlibRegister >= 0xA0 && AdlibRegister <= 0xBF ||
             AdlibRegister == AD_MASK) {

            /*
             *  See if we have the device
             */

             if (CheckDeviceAccess()) {

                 if (!WriteFile(DeviceHandle,
                                &PortData,
                                Position * sizeof(PortData[0]),
                                &BytesWritten,
                                NULL)) {
                     dprintf1(("Failed to write to device!"));
                 } else {
                    /*
                     *  Work out what status change may have occurred
                     */

                     if (AdlibRegister == AD_MASK) {

                        /*
                         *  Look for RST and starting timers
                         */

                         if (data & 0x80) {
                             Status = 0;
                         }

                        /*
                         *  We ignore starting of timers if their interrupt
                         *  flag is set because the timer status will have to
                         *  be set again to make the status for this timer change
                         */

                         if ((data & 1) && !(Status & 0x40)) {
                             dprintf2(("Timer 1 started"));
#if 0
                             Timer1Started = TRUE;
#else
                             Status |= 0xC0;
#endif
                         } else {
                             Timer1Started = FALSE;
                         }

                         if ((data & 2) && !(Status & 0x20)) {
                             dprintf2(("Timer 2 started"));
#if 0
                             Timer2Started = TRUE;
#else
                             Status |= 0xA0;
#endif
                             Timer2Started = TRUE;
                         } else {
                             Timer2Started = FALSE;
                         }
                     }
                 }
             }

             Position = 0;
         }
     }
 }


/*
 *  Gets called when the application reads from one of our ports.
 *  We know the device only returns interesting things in the status port.
 */

 void MyByteIn(WORD port, BYTE *data)
 {
     DWORD BytesRead;

     dprintf4(("Received read from Port %4X", port));

     port = (port & 1) | ADLIB_STATUS_PORT;

    /*
     *  If we fail simulate nothing at the port
     */

     *data = 0xFF;

    /*
     *  Say there's nothing there if we didn't get the device driver or
     *  it's not the status port
     */

     if (port != ADLIB_STATUS_PORT || !CheckDeviceAccess()) {
         return;
     }

#if 0 // WSS interrupt screwed this up
    /*
     *  Are we expecting a state change ?
     */

     if (Timer1Started || Timer2Started) {

        /*
         *  Read the status port from the driver - this is how the
         *  driver interprets read.
         *  Well, actually don't because the WSS driver doesn't work!
         */

         if (!ReadFile(DeviceHandle,
                       &Status,
                       1,
                       &BytesRead,
                       NULL)) {

             dprintf1(("Failed to read from device - code %d", GetLastError()));
         } else {

            /*
             *  Look for state change
             */

             if (Status & 0x40) {
                 Timer1Started = FALSE;
                 dprintf2(("Timer 1 finished"));
             }

             if (Status & 0x20) {
                 Timer2Started = FALSE;
                 dprintf2(("Timer 2 finished"));
             }
         }
     }
#endif

     dprintf3(("Data read was %2X", Status));
     *data = Status;
 }


/*
 *  Standard DLL entry point routine.
 */

 BOOL DllEntryPoint(HINSTANCE hInstance, DWORD Reason, LPVOID Reserved)
 {
     switch (Reason) {
     case DLL_PROCESS_ATTACH:
         if (!VDDInstallIOHook(hInstance, 2, ports, &handlers)) {
             dprintf2(("Ad Lib VDD failed to load - error in VDDInstallIoHook"));
             return FALSE;
         } else {
             dprintf2(("Ad Lib VDD loaded OK"));
             return TRUE;
         }

     case DLL_PROCESS_DETACH:
         VDDDeInstallIOHook(hInstance, 2, ports);

        /*
         *  Note that  this event corresponds to FreeLibrary on our DLL,
         *  NOT termination of the process - so we can't rely on process
         *  termination to close our device handle.
         *
         */

         if (DeviceHandle) {
             CloseHandle(DeviceHandle);
             DeviceHandle = NULL;      // Redundant but neater.
         }
         return TRUE;

     default:
         return TRUE;
     }
 }