A METHOD AND SYSTEM FOR DEVICE VIRTUALIZATION BASED ON AN INTERRUPT REQUEST IN A DOS-BASED ENVIRONMENT

Abstract: A technique for providing device virtualization in an MS-DOS based operating environment, using an interrupt request (e.g., a non-maskable interrupt), is described. The technique includes executing an application on a processor within the MS-DOS based operating environment and, when the application attempts to address the device to be emulated, causing a processor interrupt to occur. In response to the interrupt, the processor executes code representing the virtualization of a device. The code for servicing the interrupt and emulating the device are written in protected-mode code (310), stored in the extended memory area (510), and made available by making appropriate entries into the interrupt descriptor tables (IDTs) (310, 318) for the protected-mode contexts which exist for the native protected-mode operating systems and for the DOS extender (410). The entries made into the IDT for the protected-mode context established for the DOS extender are accomplished by intercepting communications between the DOS extender and the virtual control program interface (VCPI) and patching the DOS extender's IDT with the vectors to the stored emulation code.

Description: A METHOD AND SYSTEM FOR DEVICE VIRTUALIZATION BASED ON AN INTERRUPT REQUEST IN A DOS-BASED ENVIRONMENT COPYRIGHT NOTICE A portion of the disclosure of this patent document contains material which is subject to copyright protection, particularly Appendices A, B and C. The copyright owner has no objection to the facsimile reproduction by anyone of the patent document or the patent disclosure, as it appears in the Patent and Trademark Office patent file or records, but otherwise reserves all copyright rights whatsoever. CROSS-REFERENCE TO RELATED APPLICATIONS The present application is a continuation-in-part of United States Patent Application Serial Number 08/712,363, filed September 11, 1996, by the present assignee. The contents of that application are hereby incorporated by reference . BACKGROUND OF THE INVENTION Field of the Invention This invention generally relates to computer software and, more particularly, it relates to the use of an interrupt request for triggering device virtualization services in a DOS-based operating system environment. Description of the Prior Art MS-DOS is Microsoft's 16-bit operating system which runs on PC-XT- and PC-AT- type personal computers (PCS) . This operating system (OS) was originally developed for the Intel 8086 and 8088 microprocessors which operate in what is generally referred to as "real-mode." Real-mode refers to the way the microprocessor handles memory (i.e., providing the user with a single-tasking working environment in which programs can freely access system memory and input/output devices) . It is noted that real-mode, by itself, does not include features such as memory management or protection. Today's personal computers are equipped with more advanced processors from Intel, specifically the 80386, 80486, and Pentium, all of which are capable of running 8086-compatible code (real-mode) . These processors, however, have a more advanced 32-bit protected-mode which provides hardware support for multitasking, data security and virtual memory. Among other things, protected-mode allows access to more random access memory (RAM) than the 1 megabyte (MB) that the standard real-mode MS-DOS operating system permits (additional details described in King, A. , "Inside Windows 95", Microsoft Press, pp. 33-43, 1994, which is herein incorporated by reference) . A typical memory configuration of a personal computer is shown in Figure 1. The memory configuration of Figure 1 shows a memory area 110 containing 640 kilobytes (K) of conventional memory. Because MS-DOS is designed for managing this conventional memory, an additional memory manager is not required. All MS-DOS based programs require conventional memory. The remainder of the standard 1 MB is referred to as the upper memory area 112 which comprises 384K of memory above the 640K of conventional memory. The upper memory area 112 is generally used by system hardware, such as a display adapter. Unused parts of the upper memory area 112 are called upper memory blocks (UMBs) . On any PC, UMBs can be used for running device drivers and memory-resident programs as described below. An extended memory (XMS) area 114 is memory located beyond the 1 MB boundary on computers with 80286, 80386 or 80486 processors. Extended memory requires an extended-memory manager, such as HIMEM. A high memory area (HMA) 116 is approximately the first 64K of the extended memory area 114. The HMA 116 is a small address region, slightly less than 64K, above the 1 MB boundary that can be accessed by real-mode code. The address region of the HMA is from lOOOOOh through lOffefh. On a computer with extended memory, the setup program can install MS-DOS to run in the HMA 116. This provides more conventional memory to be available for other software programs . Another type of memory available, as shown in Figure 1, is known as expanded memory. An expanded memory board (EMS) 118 contains additional memory that some MS-DOS based applications can use. Most personal computers accommodate expanded memory. Expanded memory is installed on an expanded memory board 118 and requires an associated expanded memory manager. Computer software programs use expanded memory 64K at a time by addressing a part of the upper memory area 112 known as the EMS page frame. Because an expanded memory manager gives access to a limited amount of expanded memory at a time, using expanded memory is slower than using extended memory. A conventional software program such as EMM386 can simulate expanded memory using extended memory for programs that require it . As MS-DOS matured through the 1980s, much of its functionality was added in the form of device drivers. As set forth in the Encyclopedia of Computer Science, Third Edition, IEEE Press (1993) , which is hereby incorporated by reference, a device driver is a program or subprogram that is written to control either a particular hardware device or another software routine. This low-level software program is loaded into memory after the operating system boots and remains in memory for the duration of the operating session. As mentioned, the device driver can deal directly with the hardware of a particular device. The device driver is capable of responding to hardware and software interrupts and interfacing to the operating system through an established MS-DOS device driver protocol in order to serve as an interface between such hardware or software and the operating system (described in "Microsoft MS-DOS 6 User's Guide", Microsoft Corporation, pp. 87-89, 1993, and which is also hereby incorporated by reference) . It should be noted that device drivers are generally limited in size to 64K bytes . In addition to device drivers, MS-DOS also uses memory-resident software programs, often referred to as terminate-and-stay-resident programs (TSRs) . These programs are executed like normal software programs, but after termination, a portion of the program remains installed in memory in order to monitor and/or service hardware or software interrupts. TSRs can also interface with the operating system in a limited sense, but they do not use the MS-DOS device driver protocol. Both device drivers and TSRs are typically real- mode code and reside in memory below the MS-DOS 1 MB limit. In many cases, these resident software programs reside within the conventional 640K of memory which is normally used by MS-DOS to load and execute programs. As mentioned above, they may reside above the conventional 640K in the upper memory area 112 when, for example, a memory optimizing routine is executed such as MemMaker (additional details are provided in MS-DOS User's Guide, beginning on page 131) . In this case, the driver or TSR would be placed into the upper memory area 112 with a link to its location placed in the 640K conventional memory 110 to indicate its memory location in the upper memory area 112. Even so, as more and more device drivers and TSRs are loaded into a system, the maximum allowable memory area for MS-DOS programs, drivers and TSRs is rapidly reduced. To further illustrate the functional inter- relationships of MS-DOS, device drivers, hardware, etc., Figures 2 through 4 illustrate the evolution, from a functional perspective, beginning with the original MS-DOS real-mode environment up to present day environments allowing for protected-mode and DOS extenders . It is noted that the functional interrelationships shown in Figures 2 through 4 are well known to those of ordinary skill in the art and well documented as well; therefore, each figure is only briefly described. A more comprehensive treatment of DOS extenders and their evolution is provided in Duncan et al., EXTENDING DOS, A Programmer ' s Guide to Protected-Mode DOS, 2nd Edition, Addison-Wesley (1992) , which is herein incorporated by reference for its teaching of DOS extenders and associated aspects relevant herein. Figure 2 shows a block diagram of a conventional DOS operating environment with no memory management services. This operating environment is similar to that used in the 8086- and 8088-based PCS. The hardware 210 sends an interrupt request, represented by the dotted line 211, to the DOS operating system 212 which is initially serviced by the DOS Interrupt Vector Table 213. Based on the entries in Interrupt Vector Table 213, the interrupt request can be directed to the DOS OS 212, device drivers 214, TSRs 216, etc. Similarly, software interrupts generated by programs 218 are serviced by the DOS Interrupt Vector Table 213 and directed as appropriate. As mentioned above, with the release of more powerful Intel processors in the late 1980s, special "extensions" to the standard MS-DOS operating system were developed to allow MS-DOS programs to easily access computer memory that would not otherwise be addressable using real- mode code. These extensions (i.e., device drivers) to the operating system take advantage of the protected-mode features of the 80X86 processors while allowing MS-DOS to continue executing in real-mode. These device drivers implement, as a minimum, memory management services referred to as EMS and XMS services . These services allow software programs to store and retrieve large amounts of data from the memory pool above the 1 MB boundary. However, they do not allow software programs to occupy this memory for execution purposes . The well known programs provided by Microsoft to implement this functionality are called HIMEM- SYS and EMM386.EXE. Figure 3 shows a block diagram of a conventional DOS operating environment with conventional memory management services 312 installed for managing the extended memory area 114. This operating environment is similar to that used in 80286-based PCS through Pentium-based PCS. As shown in Figure 3, a protected-mode operating system 310 executes above the DOS operating system 212. An interrupt request 311 generated in this environment passes first into the protected-mode operating system 310 allowing for higher level management. As shown, the interrupt request 311, based on entries in an Interrupt Descriptor Table (IDT, described in more detail below) , may be directed to memory management services 312, to the Virtual Control Program Interface (VCPI) 314, to the default DOS service via the DOS Interrupt Vector Table 213, or the like, as in the Figure 2 type systems. As shown, the protected-mode OS includes the IDT 318 as well as a GDT 320. Additional details of the operation of the VCPI are described in the Virtual Control Program Interface specification, Version 1.0, (1989), which is herein incorporated by reference for its teachings regarding the operation of a VCPI . Concurrent with the development of these memory extensions was the development of DOS extenders. Figure 4 shows a block diagram of a conventional DOS operating environment with memory management services 312 installed and a DOS extender 410 running. DOS extenders are low-level software components that allow programs to be executed in protected-mode under MS-DOS. The DOS extender 410 allows protected-mode applications 412 to communicate with hardware and the operating system and vice versa. These programs can use the vast amount of memory above 1 MB to execute code as well as to store and retrieve data. However, the DOS extenders do not allow executable code to permanently reside in memory above the 1 MB boundary; rather, upon switching into protected-mode, the DOS extenders can temporarily copy executable code into the extended memory area for purposes of execution, but upon termination, it no longer exists in extended memory. Even so, the DOS extenders allow programs to apply a much simpler and much more powerful programming model . DOS extenders achieve their task by switching the processor into protected-mode while running the program, and switching the processor back to real-mode when the program completes. The DOS extenders also switch the processor in and out of real -mode during the program execution when MS-DOS functions need to be called, or when device drivers or TSRs need to run. To do this, the DOS extender must be in complete control of the system. Thus, a new protected-mode operating environment is created by the DOS extender. As shown in Figure 4, the newly created protected-mode operating environment created by the DOS extender 410 includes its own IDT 418 and GDT 420. It is noted, however, that when the DOS extender switches back to real mode it then uses system IDT 318 and system GDT 320. In systems with memory management enabled, as shown in Figures 3 and 4, the device drivers that provide these services create their own protected-mode environment and already have MS-DOS running as a (virtual 8086) real- mode task. Therefore, they provide contention management services to negotiate memory allocation and mode switching between themselves and DOS extenders. This contention management is implemented by all memory managers today via the VCPI 314. If a DOS extender 410 determines that a VCPI 314 is present in a system, it uses this interface to acquire its protected-mode context and to perform its mode switching (real versus protected) . In most cases, the DOS extender 410 also allocates all of its required program memory through the VCPI 314. With this evolution in mind, as available memory, as well as processing power, continue to increase, it is desirable to make efficient use of these computing resources to provide device virtualization, thereby reducing the amount of hardware necessary to provide desirable features. The 80386 and later processors do generally provide for hardware virtualization by allowing certain instructions to be "trapped." Specifically, if a program executes an. "IN" or "OUT" (I/O) instruction, a General Protection Fault handler can be invoked to decide whether or not to let the IN/OUT execute and/or whether or not to emulate the presence of some hardware. This "I/O Trapping" can be applied to any process running below processor privilege level (or ring) 0. Unfortunately, most DOS extenders (Rational Systems' D0S4GW for example) run at ring 0, thus they are not subject to the 80386 I/O trapping mechanism. In addition to the 80386 I/O trapping mechanism, at least one hardware manufacture has used a combination of external hardware interrupts and software to replace the functions of more costly hardware. Specifically, Advanced Gravis has used a technique which combines a Non-Maskable Interrupt (NMI) and a DOS TSR to virtualize hardware. Although this technique provides hardware virtualization for applications executing in real-mode, it does not provide virtualization for applications executing under a DOS extender, since there is no predetermined method for the operating system to direct the interrupt services of the DOS extender . There is, therefore, a real need to provide an efficient way of performing device virtualization which is also accessible by DOS extenders. SUMMARY OF THE INVENTION The present invention involves a technique for providing device virtualization in a DOS based operating environment including the steps of executing an application, under a DOS extender, on a processor within the DOS-based operating environment, where the application executes within a protected-mode context created for the DOS extender and the protected-mode context includes an interrupt descriptor table (IDT) . A processor interrupt is generated when the application addresses a predetermined address related to the device to be emulated and, responsive to the processor interrupt, an appropriate entry in the IDT associated with the protected-mode context of the DOS extender is referenced. In accordance with the invention, the DOS extender's IDT is patched at run-time to vector to device emulation software, thereby allowing the processor to execute computer code designed to emulate the device even though the DOS extender was not present at boot time. The IDT is patched at run-time in accordance with the invention by intercepting VCPI far-calls or, in a presently preferred embodiment, by also intercepting the VCPI "Switch to Protected Mode" call. In particular, a new VIVO driver of the invention intercepts Int 67h handler calls from DOS virtual 8086 (real mode) software. Int 67h handler, function deOch, is the VCPI "Switch to Protected Mode" call which is made by the DOS extender when it is in real mode, running under the system's protected mode context. It is a request from the DOS extender asking the VCPI to switch the DOS extender into protected mode and to apply the DOS extender's protected mode context to the system. The VIVO driver also intercepts VCPI far calls by monitoring Int 67h handler, function deOlh, which is the VCPI "Get Protected Mode Interface" call. In accordance with the presently preferred embodiment of the invention, the DOS extender makes the VCPI "Switch to Protected Mode" and "Get Protected Mode Interface" calls at system startup and many times after that during the normal execution of the protected mode application running under the DOS extender. Because the DOS extender is asking the VCPI to apply a new protected mode context, the DOS extender must tell the VCPI what that context is, and so it passes the VCPI (among others) at least the following: physical address of the Page Directory, linear address and size of the DOS extender's GDT and the DOS extender's IDT, and the like. These arguments tell the VIVO driver of the invention where the DOS extender's GDT and IDT are. By temporarily switching the processor's Page Directory, the VIVO driver can access the DOS extender's IDT and patch it with the appropriate vector to the stored emulation code. In particular, the VIVO driver of the invention performs the following steps to patch the DOS extender's IDT during run-time. First, the VIVO driver intercepts Int 67h, function deOch (switch to protected mode) . The VIVO driver then switches the processor's page directory to that of the DOS extender, which was specified by the DOS extender in the VCPI call. This allows the VIVO driver to properly access the DOS extender's GDT and IDT at run-time. The VIVO driver then checks to see if its IDT entries have already been made into the DOS extender's IDT. If so, it checks to see if the selector entry in the DOS extender's IDT indicates the appropriate entry in the DOS extender's GDT, where the selector entry includes the GDT entries that get installed into the DOS extender's GDT when the VCPI get protected mode interface is called by the DOS extender. If either the DOS extender's IDT entry does not exist, or the selector entry does not match the DOS extender's GDT entry, the VIVO driver's code searches the DOS extender's GDT to find out what selector value corresponds to the VIVO emulation code and then makes the appropriate DOS extender IDT entry using the selector value. The VIVO driver then switches the processor page directory back to that of the system's protected mode context and jumps to the real VCPI switch call . The patched DOS extender IDT may now be used to emulate the device that caused the interrupt to be generated. BRIEF DESCRIPTION OF THE FIGURES The above and other beneficial features and advantages of the invention will become more apparent from the following detailed description of the invention, of which: Figure 1 is a block diagram of a conventional memory configuration of a personal computer. Figure 2 is a high-level functional block diagram of a conventional DOS operating environment. Figure 3 is a high-level functional block diagram of a conventional DOS operating environment with memory management services installed. Figure 4 is a high-level functional block diagram of a conventional DOS operating environment illustrating the functional relationships of DOS with memory management services and DOS extenders . Figure 5 is a high-level functional block diagram of a DOS operating environment as it relates to an exemplary embodiment of the present invention. Figure 6 is a flow diagram of the initialization operation, at boot time, of an exemplary embodiment of the present invention. Figure 7 is a flow diagram of the operation of an exemplary embodiment of the present invention, at run time, to provide compatibility with DOS extenders by intercepting VCPI far calls. Figure 8 is a flow diagram of the operation of a presently preferred embodiment of the present invention, at run time, to provide compatibility with DOS extenders by intercepting VCPI "Switch to Protected Mode" calls. DETAILED DESCRIPTION OF THE EXEMPLARY EMBODIMENTS Overview As mentioned above in the Background section, device virtualization, in general, is known. Unfortunately, when running in the context of DOS extenders (e.g., Rational Systems ' D0S4GW) , device drivers cannot take advantage of the I/O trapping mechanism, nor can they take advantage of Advanced Gravis technique mentioned above in the Background section. Because many present day applications use DOS extenders for various reasons, a device virtualization technique should be accessible by applications running under DOS extenders . Generally, the present invention provides device virtualization, in an MS-DOS based operating environment, by way of an interrupt request (e.g. non-maskable interrupt) for applications running with or without a DOS extender. The present invention dynamically extends its device virtualization capabilities to applications running under DOS extenders by intercepting communications between the DOS extender and the VCPI at run-time. In particular, when an executing application requests the use of a particular device (e.g., requesting a predetermined I/O address), a processor interrupt request is generated. In response to the interrupt request, the device virtualization code for that particular device can be accessed and executed even for applications running under DOS extenders. It is noted that, in the exemplary embodiment, the present invention relies on the presence of a memory manager, or an equivalent thereof, to provide for a primary protected-mode operating environment including the system IDT and system GDT. Figure 5 is a block diagram illustrating the functional relationship between conventional operating system components in a typical MS-DOS environment as described in the Background section with reference to Figures 2 through 4 and is an exemplary embodiment of the present invention. As shown in Figure 5, the present invention functionally represents a new functional layer working in conjunction with the protected-mode operating system 310. This new layer is referred to, for purposes of this specification, as VIVO driver 510. Essentially, the VIVO driver 510 becomes an extension of the protected-mode operating system that supervises MS-DOS and DOS extenders instead of simply becoming an extension of MS-DOS itself, as is the case with standard device drivers and TSRs . In the exemplary embodiment of the present invention, in addition to VIVO driver 510 which represents a driver designed to provide the device virtualization services, a VIVO TSR 520 is provided to aid in the dynamic extension of the services by the VIVO driver 510 for use with DOS extenders 410. The present invention includes loading protected- mode executable code, including an interrupt service routine and device virtualization code, into memory, and entering the associated interrupt service vector directly into the protected-mode operating system's interrupt descriptor table (IDT) 318. The use of IDTs is well known and additional details of descriptor addressing are described beginning on page 41 of the Windows '95 reference incorporated herein. Additional teachings on the use of IDTs can be found in Microprocessors, Intel (1989) , which is also herein incorporated by reference. Briefly, the protected-mode IDT 318 determines interrupt vectoring instead of the DOS Interrupt Vector Table 213 even when the protected-mode operating system 310 is running real-mode code. In many cases, such as MS-DOS operating system calls, the services indicated by the IDT entries in IDT 318 simply pass control to the real-mode services indicated by the DOS interrupt vector table that were installed by MS-DOS. As well as being faster and more efficient than real-mode interrupt handlers, using the protected-mode IDT 318 makes the services of the present invention unconditionally available to the system whether it is running in real- or protected- mode . The present invention, by way of the VIVO TSR 520, uses the VCPI "Get Protected-mode Interface' real-mode call (Int 67h, function deOlh) or the VCPI "Switch to Protected Mode' real-mode call (Int 67h, function deOch) to intercept VCPI calls to patch the DOS extender's IDT. Initially, the strategy was to force all of the DOS extender's 410 subsequent VCPI 314 protected-mode "far calls" to "pass through" the VIVO driver 510, where the "Get Protected-mode Interface" was used by a DOS extender 410 at startup to acquire a protected-mode context. When the VIVO driver 510 intercepted these far calls, it was running under the context of the DOS extender's protected-mode operating system (IDT 418, GDT 420) . At this point, the VIVO driver 510 installed its interrupt service vector into the DOS extender's IDT 418 making the VIVO driver's device virtualization service available to the DOS extender 410 (and its applications) until it terminates. However, in accordance with a presently preferred embodiment of the invention, the strategy for making the actual patch entry into the DOS extender's IDT 418 is changed. Rather than patching the DOS extender's IDT 418 during any VCPI far call intercept, the patch is now performed at the intercept of the VCPI "Switch to Protected Mode' call (Int 67h, function deOch) . In this fashion, application programs running under DOS extenders are prevented from generating I/O related interrupts after the switch to protected mode but before any VCPI far calls are made. Although the present invention is designed to operate using most any interrupt request, the exemplary embodiment of the present invention uses the non-maskable interrupt (NMI) as the interrupt request for triggering the device virtualization routine. It is noted that, historically, the NMI has been used by system motherboard logic to notify the operating system of a RAM (memory) parity error, or by I/O devices to notify the operating system of some "unrecoverable" error. Today, however, the RAM parity no longer exists and I/O devices do not implement NMI generation. A typical DOS extender's NMI handler, however, not knowing specifically how to handle such an event, simply re-boots the system rather than "passing-down" the interrupt to the currently installed DOS handler as it would do with a normal interrupt. Therefore, unlike other interrupt services, an NMI interrupt service routine installed under DOS would most likely be ignored by an application running under a DOS extender. Description of Exemplary Embodiments Although illustrated and described below with reference to certain specific embodiments, the present invention is nevertheless not intended to be limited to the details shown. Rather, various modifications may be made in the details within the scope and range of equivalents of the claims and without departing from the spirit of the invention. During operation of the embodiment of Figure 5, when an interrupt request 311 (e.g., NMI) occurs, the protected-mode IDT 318 directs the flow of operation to the appropriate service routine based on the entries therein. If the interrupt is not intended for the VIVO driver 510, then the interrupt is passed on to the normal flow of operation. However, when the VIVO driver 510 of the present invention is present with a DOS extender 410 running, the interrupt request is not passed directly to the DOS extender 410. Rather, if the interrupt request is intended for the VIVO driver 510, the VIVO driver 510 acts on the request directly. If the request is not for the VIVO driver 510, then control is passed to the DOS extender 410, and the DOS extender 410 handles it in its normal way. It is noted that, in the exemplary embodiment of the present invention, because the NMI is used, the device virtualization can be performed immediately following the "faulting" instruction (i.e., the instruction which caused the NMI) . Turning to the generation and installation of the present invention, after the computer code embodying the present invention is written, it is compiled/assembled and linked, in the exemplary embodiment, as zero address-based, non-relocatable, flat-model, 32-bit protected-mode code. The computer code embodying the present invention is then saved as a standalone binary file. It is noted that, in preparing the computer code embodying the present invention, protected-mode code is required (at least at the NMI service entry point) since the protected mode IDT's vectoring mechanism does not allow vectoring to real-mode (or virtual 8086) code. It is also noted that, although some real-mode code is required, the exemplary embodiment of the present invention is implemented using almost all protected-mode code since it is more efficient for vectoring and execution and can be completely located in extended memory, thereby avoiding memory-hungry DOS real-mode applications. Basically, the VIVO driver 510 includes the device virtualization code for emulating the particular device and it includes the code necessary to receive, at run time, the re-directed "far calls" and "Switch to Protected Mode" calls from the DOS extender 410, to make the appropriate entries in the IDT 418, and to pass control on to the VCPI 314. It also includes the Int 67h handler which is used to reflect the VCPI "Get Protected-Mode Interface" call return or the VCPI "Switch to Protected Mode" call return into the VIVO TSR 520. An exemplary implementation of a VIVO driver 510 which intercepts a "Switch to Protected Mode" call (Int 67h, function deOch) and a "Get Protected Mode Interface" call (Int 67h, function deOlh) in accordance with a presently preferred embodiment of the present invention is included as Appendix A. An exemplary implementation of a VIVO driver 510 which intercepts only the "Get Protected Mode Interface" call (Int 67h, function deOlh) is included as Appendix A to the parent application, U.S. Patent Application Serial No. 08/712,363, the contents of which are hereby incorporated by reference . The installation and operation of the present invention is described with reference to the flowcharts in Figures 6-8. As shown in Figure 6, at boot-time, in the exemplary embodiment of the present invention, an MS-DOS initialization program (e.g., ssinit.com) allocates a predetermined amount of the extended memory area 114 above the HMA 116 using an extended memory (XMS) interface reference, at step 610. An exemplary implementation of ssinit.com suitable for use with the present invention is included as Appendix B. The predetermined amount of memory, in the exemplary embodiment of the present invention, is the size of the 32-bit protected-mode code file. Additionally, in the exemplary embodiment, the allocated memory is below the 4 Megabyte boundary. Then, the allocated memory, using the XMS interface, is locked and the physical address is saved. Subsequently, the code embodying the present invention is copied into the allocated portion of extended memory, at step 612. Appropriate entries are made into the protected-mode system IDT 318 and system GDT 320 to allow the associated hardware or software interrupt to vector directly to the 32-bit service entry point (s) in extended memory, at step 614. It is noted that since the interrupt vectoring is done at the protected-mode level, the 32-bit services are always available and can be initiated by the protected-mode operating system 310 without explicitly performing any time-consuming mode switching. More specifically, steps 612 and 614 are accomplished as follows: The protected-mode code file embodying the present invention is opened and a 1 k-byte portion of the code is loaded into local memory. In the exemplary embodiment of the present invention, this first portion of the code is loaded because it has variables that need to be initialized at predetermined offsets which can only be determined at this stage of loading the program (e.g., configuration parameters) . Next, the VCPI "Switch to Protected Mode" call (Int 67h handler, function deOch) and the VCPI "Get Protected Mode Interface" call (Int 67h, function deOlh) is invoked. Three selectors and an entry point offset for the "Get Protected Mode Interface" call are returned. The three selectors and the entry point offset are copied into a predetermined offset in the first portion of the protected- mode code. Also, the segment and offset of a portion of the MS-DOS initialization code is copied into a predetermined offset in the first portion of the protected-mode code. This referenced portion of the MS-DOS initialization code remains resident after the MS-DOS initialization code terminates and the protected-mode code, in the exemplary embodiment, will need to know where it is because it is essentially a shared dataspace used by the VIVO TSR 520 and the VIVO driver 510 to communicate. Next, the first portion of the protected-mode code is copied into the allocated extended memory (e.g., using an XMS interface) and then the rest of the protected-mode code embodying the present invention is copied into extended memory in the same manner. Next, the linear addresses for the protected-mode system IDT 318 and system GDT 320 and the physical address for the Page Directory (using standard 386 instructions) are obtained and saved. A protected-mode initialization program (e.g., a DOS4GW executable) is spawned and the physical address of the allocated Extended Memory, GDT and IDT linear addresses, as well as the Page Directory physical address, are passed thereto. An exemplary implementation of the D0S4GW executable suitable for use with the present invention is included as Appendix C. The functions of the protected-mode initialization program, in the exemplary embodiment of loading the present invention, are to: 1) Make three entries at the top of the protected mode operating system's GDT 320. The first entry is a 32-bit code selector with a linear address based at the beginning of the allocated Extended Memory. The second entry is a data selector to alias the first entry. The third entry is a 32-bit data selector that is based at linear address 0 and has a maximum size (or limit) . 2) Save the selector base of the entries made in step 1. 3) Make an entry into the protected mode operating system's IDT 318 for vector 02h (the NMI vector) . This vector uses the selector base saved above with an offset of zero (in the exemplary embodiment, the NMI entry point in the 32 -bit code) . 4) Save the original Int 67h handler vector. 5) Make an entry into the protected mode operating system's IDT 318 for vector 67h (the Int 67h vector) . This vector uses the selector base saved above with an offset of eight (the Int 67h handler intercept entry point in the 32 -bit code) . 6) Then, return control to the MS-DOS initialization program (Appendix B) . Finally, the MS-DOS initialization program terminates, leaving a relatively small program in memory -- referred to above as the VIVO TSR 520. The general technique of leaving a TSR in memory upon initialization program termination is a well known technique to those of ordinary skill in the art. By way of the Int 67h handler entry, the VIVO driver 510 is linked into the VCPI service/control chain via the IDT 318 for Interrupt 67h so that the hooks are present for the present invention to extend its functionality to DOS extenders 410 when they initialize. It is noted that Interrupt 67h is a control function interface to the EMS 118 and VCPI 314. This Interrupt 67h handler (VIVO driver 510) and VIVO TSR 520 act upon VCPI functions, and then pass control to the normal Interrupt 67h handler installed by the protected-mode operating system. At this point, the protected-mode operating system 310 is configured such that hardware virtualization is available to all real -mode programs and drivers. There are also now hooks in place to allow for the extension of the device virtualization service to applications running under DOS extenders. It is noted that, in the exemplary embodiment of the present invention, although the hooks are put in place during boot time, the extension of the device virtualization services to applications running under DOS extenders is completed at run-time. This is an important aspect of the present invention becomes it allows the present invention to avoid many of the drawbacks of the prior art techniques . It is noted that, although the above-described initialization process is performed using a combination of the MS DOS initialization program and the protected-mode initialization program, in an alternate embodiment, all of the initialization functions performed by the MS DOS initialization program can be performed by the protected- mode initialization program. To do so, the MS DOS initialization program (e.g., Appendix B) needs to pass an extra parameter to the protected-mode initialization program. The extra parameter is the real-mode address of the shared data space within the DOS initialization program. The way in which the VIVO code of the present invention extends its functionality to DOS extenders via the Interrupt 67h handler is described with reference to the flowcharts of Figures 7 and 8. In a first embodiment of the invention, as shown in Figure 7, after having established the VIVO TSR 520, it is triggered when, at run time, the DOS extender 410 calls the VCPI "Get Protected-Mode Interface" function, at step 712, via Int 67h. At this point, the DOS extender 410, initially in real-mode, expects to acquire the protected- mode far-call entry point to the VCPI 314. This entry point is used by the protected-mode operating system created by the DOS extender 410 to communicate directly with the VCPI 314 without invoking software interrupts and without switching to real mode. However, the VIVO TSR 520 of the present invention, having been passed control indirectly, by the VIVO driver 510, at this point gives the DOS extender 410 an entry point to the VIVO driver 510, at step 714. It is noted that, in this embodiment of the present invention, the VIVO TSR 520 relies on the presence of a VCPI 314, or equivalent thereof, for cooperation with DOS extenders 410. By providing DOS extender 410 with an entry point into the VIVO driver 510 rather than the actual VCPI entry point, the DOS extender 410 will now pass through the VIVO driver 510 on subsequent "far calls." When the DOS extender 410 makes these VCPI "far calls, " the system is in the DOS extender's IDT/GDT protected mode context As such, when the VIVO driver 510 intercepts the VCPI far-calls, it makes its own interrupt vector entries into the DOS extender's protected-mode operating system IDT 418, at step 716, similar to that which was done for the native protected-mode operating system 310. After making the appropriate entries, the VIVO driver 510 then passes control to the actual VCPI 314 via the previously saved VCPI protected-mode far-call entry point, at step 718. More specifically, steps 714, 716 and 718 are accomplished as follows: The VIVO driver 510 intercepts the Get Protected- Mode Interface call at the Int 67h entry point (all other Int 67h calls are passed through to the default handler) . The VIVO driver 510 saves the real-mode return address for the DOS extender 410 into the shared data space. It then changes the real-mode return address (stored on the processor stack) so that, when the actual VCPI service completes, control will pass to the VIVO TSR 520. So, essentially, all Int 67h calls still get processed by the default handler except a "Get Protected-Mode Interface" call causes the change of the real-mode return address, by the VIVO driver 510, prior to being processed by the default handler. It is noted that, in an alternate embodiment, the function of intercepting the initial Int 67h call from the DOS extender 410 may be performed by VIVO TSR 520. This may be problematic, however, because when the present invention runs under a memory manager, such as QEMM by Quarterdeck Office Systems, the memory manager does not pass the VCPI Int 67h calls through the DOS Interrupt Vector Table, but rather, fields all VCPI Int 67h calls in protected mode via the IDT 318 . As shown in Figure 7, in the case of the "Get Protected-Mode Interface" call, control is then passed to the VCPI 314 which performs its service. The VCPI 314 returns control, via the new return address, to the VIVO TSR 520. At this point, a page table context has been setup in the DOS extender's data space. The VIVO TSR 520 then makes additional page entries so that the VIVO driver 510 is valid within the DOS extender's protected-mode context. The VIVO TSR 520 then copies the three selectors being passed back to the DOS extender 410 into the shared data space as well as the VCPI far call entry offset. Next, the VIVO TSR 520 copies its own three selectors (described above) into the DOS extender's return data space. It then alters the VCPI entry offset (in register ebx) to 16, which is the offset into the VIVO driver 510 for the VCPI far can intercept. Finally, it returns control to the original real -mode return address (the DOS extender 410) saved above in the shared data space . At this point, all VCPI far calls made by the DOS extender 410 now pass control to the VIVO driver 510 within the context of the DOS extender's new protected-mode operating system. These calls include allocating extended memory for the DOS extender application and (temporarily) switching back to real -mode to service DOS interrupts and DOS operating system calls. Whenever a VCPI far call is made, an interrupt entry is made in IDT 418 by VIVO driver 510. With this completed, device virtualization (e.g., hardware virtualization) services are available to the DOS extender's protected-mode application program as well as to the normal DOS real -mode programs (i.e., VIVO driver 510 is accessible by programs running under DOS extender) , at step 720. In a presently preferred embodiment of the invention, the strategy for making the actual patch entry into the DOS extender's IDT 418 is changed from that just described with respect to Figure 7. Instead of patching the DOS extender's IDT 418 during any VCPI far call intercepts, the patch is instead performed at the intercept of the VCPI "Switch to Protected Mode" call (Int 67h, function deOch) . In the Figure 7 embodiment, the VIVO code always intercepts Int 67h calls to monitor VCPI startup activity by intercepting the "Get Protected Mode Interface" call and passing all other calls directly to the default VCPI handler. In accordance with the presently preferred embodiment (Figure 8) , however, the VIVO code also watches for the "Switch to Protected Mode" call. If this call is made, the DOS extender IDT 418 is patched then, and control is passed to the default VCPI handler. In the Figure 7 embodiment, the DOS extender's IDT 418 was patched during VCPI far calls since it was known that the DOS extender's protected mode context would be invoked at the time of the call. Unfortunately, the inventor has found that, under some circumstances, the DOS extender's application can execute code after the switch to protected mode but before any VCPI far calls are made. As a result, the DOS extender's application could generate an I/O related interrupt event before the VIVO code makes its dynamic DOS extender IDT patch in accordance with the invention. Accordingly, in accordance with the embodiment of Figure 8, the VIVO code makes its dynamic DOS extender IDT patch before the first switch to protected mode. It will be appreciated by those skilled in the art that the DOS extender must make a "Switch to Protected Mode" call at least once before any of the DOS extender's application code runs. When the DOS extender makes this call, it is executing in virtual 8086 mode and under the system's protected mode context (IDT 318, GDT 320) . To make the switch to the DOS extender's protected mode, the DOS extender calls the VCPI interface via Int 67h (function deOch) , at step 812, and provides the VCPI interface with a list of parameters that indicate what the DOS extender's, protected mode context is to be and where to begin executing protected mode code under the DOS extender's protected mode context, at step 814. More specifically, the DOS extender provides to the VCPI its Page Directory address, GDT and IDT addresses, LDT address, TSS (Task State Segment) , and protected mode program execution address . This is all the information that the VIVO Int 67h intercept code needs to be able to successfully patch the DOS extender's IDT 418 at step 816 before it ever gets into protected mode. The real VCPI switch handler is only given control at step 818 after the VIVO Int 67h intercept code is sure that the DOS extender's IDT is properly patched. With this completed, device virtualization (e.g., hardware virtualization) services are available to the DOS extender's protected-mode application program as well as to the normal DOS real-mode programs (i.e., VIVO driver 510 is accessible by programs running under DOS extender), at step 820. In the embodiment of Figure 8, because the DOS extender is asking the VCPI to apply a new protected mode context, the DOS extender must tell the VCPI what that context is. Accordingly, it passes the VCPI (among others) at least the following: physical address of the Page Directory, linear address and size of the DOS extender's GDT and the DOS extender's IDT, and the like, at step 814. These arguments tell the VIVO driver of the invention where the DOS extender's GDT and IDT are. By temporarily switching the processor's Page Directory at step 816, the VIVO driver can access the DOS extender's IDT and patch it with the appropriate vector to the stored emulation code. In particular, the VIVO driver of the Figure 8 embodiment performs the following steps at step 816 to patch the DOS extender's IDT. Once the VIVO driver intercepts Int 67h, function deOch (switch to protected mode) , the VIVO driver then switches the processor's page directory to that of the DOS extender, which was specified by the DOS extender in the VCPI call. This allows the VIVO driver to properly access the DOS extender's GDT and IDT at run-time. The VIVO driver then checks to see if its IDT entries have already been made into the DOS extender's IDT. If so, it checks to see if the selector entry in the DOS extender's IDT indicates the appropriate entry in the DOS extender's GDT, which is the GDT entry that gets installed into the DOS extender's GDT when the VCPI get protected mode interface is called by the DOS extender. If either the DOS extender's IDT entry does not exist, or the selector does not match the DOS extender's GDT entry, the VIVO driver's code searches the DOS extender's GDT to find out what selector value corresponds to the VIVO emulation code and then makes the appropriate DOS extender IDT entry using the selector value. The VIVO driver then switches the processor page directory back to that of the system's protected mode context and jumps to the real VCPI switch call (step 818) . The patched DOS extender IDT may now be used at step 820 to emulate the device that caused the interrupt to be generated. Thus, in the embodiments of Figures 7 and 8, the startup of a DOS extender is dynamically detected by monitoring the VCPI calls and dynamically patching the DOS extender's IDT when such startup is detected. In accordance with the embodiment of Figure 8 (Appendix A) , the VIVO driver continues to receive the VCPI far calls even though it does not patch the DOS extender's IDT 418 at the time of the far call . This is necessary because the DOS extender gets the selectors (GDT entries) when the DOS extender makes the VCPI "Get Protected Mode Interface" call. A far call is still necessary so that the selectors are guaranteed a "home" in the DOS extender's GDT 420. If the selectors could not be placed in the DOS extender's GDT 420, the DOS extender's application could never vector to the stored device emulation code since these selectors are the protected mode "pointers" to the device emulation code. Since the DOS extender is given these entries during the "Get Protected Mode Interface" call, the DOS extender cannot be prevented from calling the VIVO driver when it thinks it is making VCPI far calls. Hence, in the preferred embodiment, the VIVO TSR 520 monitors both the "Switch to Protected Mode" and "Get Protected-Mode Interface" calls. The VIVO driver 510 of the present invention, like other device drivers and TSRs, implements software services for hardware interrupt events. However, the VIVO driver 510 in accordance with the present invention is different than standard MS-DOS device drivers and TSRs. For example, the memory-resident, executable code of the VIVO driver 510 of the present invention can permanently reside above the 1 MB memory boundary and above the HMA 116. As such, the VIVO driver 510 does not compete for valuable memory space with standard MS-DOS programs and other device drivers and TSRs. Moreover, the VIVO driver 510 is not limited to the 64K restriction of a typical device driver allowing for applications significantly larger than 64K which is particularly useful when, for example, one desires to emulate hardware with software . Commercial Embodiment A commercial embodiment in accordance with the present invention is the Ensoniq ® SoundScape™ sound card VIVO drivers. In this commercial embodiment, a VIVO driver is used to perform the function of hardware which previously existed on a sound card. By replacing the hardware with a VIVO driver, the space consumed and cost of the sound card are significantly reduced. In operation, when an application requests access to the sound card, the request is directly processed by the VIVO driver and the functionality of that hardware element is performed in software rather than hardware. This is possible, in part, because present generation microprocessors (e.g., Pentium) are so powerful that they typically have considerable idle time which can be used to execute the VIVO driver, which appears like an application, without a noticeable delay in other necessary processing. This is only one example of how the use of a VIVO driver provides substantial advantages to not only optimizing conventional memory space but also optimizing the size and cost of an auxiliary computer card. In particular, in the commercial embodiment, if an application attempts to address the sound card in the processor I/O address space, normally 220h through 22fh, an NMI is generated. As a result, the VIVO driver performs functional emulation of various hardware previously residing on the card (e.g., the Sound Blaster) in response to I/O writes and, during certain I/O reads, the handler alters the hardware I/O return value (in the Intel 386, register EAX) in order to emulate or virtualize the presence of the hardware in the system. Since the commercial embodiment of the present invention optionally alters the processor register state, it is necessary that the NMI service routine execute immediately following the faulting I/O instruction. Although illustrated and described above with reference to certain specific embodiments, the present invention is nevertheless not intended to be limited to the details shown. Rather, various modifications may be made in the details within the scope and range of equivalents of the claims and without departing from the spirit of the invention. For example, the invention may be triggered by hardware or software interrupts. APPENDIX A This module contains the I/O virtualization handlers for both DOS and win environments. Define WIN-.C0DE to build for win/win95 environment. Also optionally included in this module are virtual PIC and DMA handlers for DOS. .586p IFDEF WIN--.CODE INCLUDE vmm.inc INCLUDE debug. inc INCLUDE vpicd.inc INCLUDE vdmad.inc INCLUDE sndscape.inc EXTERN gpSSI:DWORD EXTERN HARDWARE_PhysMemwrite: NEAR EXTERN DisableBusMasτer: NEAR EXTERN EnableβusMaster: NEAR PUBLIC ActiveCount PUBLIC NMlService PUBLIC iRQService PUBLIC PicFlags PUBLIC DmaFlags PUBLIC DacFlags PUBLIC Sbl Flags PUBLIC MpuFlags PUBLIC sblStereo PUBLIC SblTC ELSE .MODEL flat IFDEF DOS_DEBUG PUBLIC DbPUtβ PUBLIC DbPutlβ PUBLIC DbPut32 ENDIF ENDIF INCLUDE cone. inc INCLUDE iohndlrs.inc EXTERN _synth_init: NEAR EXTERN --.syπth_process_MiDl: NEAR EXTERN _synth_an_notes_off: NEAR EXTERN _syπth_set---MT32: NEAR EXTERN _fm_process: NEAR EXTERN _synth_update: NEAR EXTERN _synth_rei ni t_.voi ces : NEAR EXTERN _MT32: BYTE EXTERN _num_active-_voices: BYTE EXTERN _fm_array: BYTE IFNDEF WIN_CODE _TEXT SEGMENT ASSUME fs -NOTHING ELSE VxD_LOCKED_CODE_SEG ENDIF IFNDEF WIN_CODE ; NMI entry point here - jump over the hardware config and stub data. ; Do NOT insert any code or data before this, dammit, jmp NMlService ALIGN 4 ; Int 67h entry point here - skip to its service jmp int67Service ALIGN 4 ; VCPI entry point here - skip to its service jmp vepiService ALIGN 4 jmp Debugservice here all other data at end r r do not separate these sections HWConfig HW.CONFIG < OFFSET SynthBuffer, ?, ?, ?, ?, ?, ?, ?, ?, ?. , ? > do not separate these sections wavelRQ ask DB ? wavelRQVector DB ? waveSpecEOI DB ? ENDIF end/back-end code ... ; save regs, setup ds and cfg ptr Read the NMI " status port. The format of this WORD register is- NULL BITS DEVICE ADDR OFFSET W/-R INT STATUS 15 14 13 12 11 10 09 08 07 06 05 04 03 02 01 00 1 1 1 1 1 02 Dl DO A4 A3 A2 Al Aθ W 0 ~I where Dx is the device encode, AX is address offset, w is the write access flag, and ~ι is the inverted NMI status flag. mov d , [esi . ssi_wiOAddressConcert] add dl,CONC_wNMlSTAT_OFF ; read the status register call DisableBusMaster in ax,dx call EnableBusMaster test al.Olh ; test for our NMI jnz SHORT NMiεxit ; if π0 ne, bail mov edx , eax copy status reg shr eax, 6 shift-down dev encode bits and edx,0000007fh mask-off non address/w bits and eax.OOOOOOlch mask-off non dev encode bits mov eax,DevTable[eax] get the device table base jmp DWORD PTR [eax+edx] jump to the appropriate service NMiExit: mov dx, [esi .ssi_wiOAddressconcert] add dl,CONC_wNMiSTAT_OFF ; clear our NMI assertion out dx,al in al,61h reset system NMI latch and al.Ofh or al.Och out 61h,a. and a. ,03h out 61h,al mov eax , EaxSave restore regs pop esi pop edx pop ebx pop ds pop ecx iretd MpuDataR LABEL NEAR mov al ,MpuData mov BYTE PTR EaxSave,al btr MpuFlags ,MPU_FLAG_DPEND jc SHORT mdrmo or MpuStat,80h jmp NMiExit mdrmo: mov al .MpuTe p call MpuPutData jmp NMIEXIt Mpuoataw LABEL NEAR ;mov ah.Odh ;mov al , cl ;call DbPutlβ bt MpuFlags ,MPU_FLAG_DOUT jnc mdwexi t ; parse MIDI data here and accumulate a message test cl,80h jnz mdwstat cmp BYTE PTR Midi essage,OfOh jae mdwexit xor edx,edx mov dl.MidiCurrC mov BYTE PTR MidiMessage[edx] ,cl cmp dl.MidiDataC jb SHORT mciwmore mov MidiCurrc.Olh ; At this point we have accumulated a full MIDI message . ; put it in eax and pass it to the synth. The format will be: • eax: d31-d24 d23-dl6 dl5-d08 d07-d00 MIDI: ZERO DATA2 DATA1 STATUS mov ActiveCount,400 bts MpuFlags,MPU_FLAG--.SYNTHON jc SHORT midcall mov _num_active_voices , 24 call SynthOn midcall : mov ecx.ds mov SSoffset.esp mov SSselector.ss mov esp,OFFSET stackTop mov ss.ecx push es mov es , ecx eld push MidiMessage call _synth_process_MlDI add esp,4 pop es Iss esp.SpSave jmp NMiExit mdwmore: inc dl mov Midicurrc.dl jmp NMiExit ; got a MIDI status byte ... mdwstat : cmp cl.OfOh jae SHORT mdwsyst mov BYTE PTR MidiMessage, cl mov MidiCurrc.Olh mov al,02h mov dl.cl and dl.OeOh cmp dl.OcOh jne SHORT mdwncfx dec al mdwπcfx : mov MidiDataC.al jmp NMiExit mdwsyst: cmp cl.Ofβh jae SHORT mdwexit mov BYTE PTR MidiMessage,cl mdwexi t: jmp NMiExit MpuStatR LABEL NEAR mov al.MpuStat mov BYTE PTR EaxSave, al jmp NMiExit MpuCmdW LABEL NEAR ;mov ah.Och ;mov al , cl ;call DbPutlδ cmp cl.Offh jne SHORT mewnrs or MpuStat.βOh btr MpuFl ags ,-MPU_FLAG_DPEND btr MpuFl ags , MPU_FLAG_DOUT btr MpuFl ags , PU_FLAG_UART jc SHORT mcwnak mov al . Ofeh call Mpu Put Data mcwnak: ; do synth shutdown stuff for MPU reset mov ecx. ds mov ssoffset. esp mov SSselector. ss mov esp, OFFSET stackTop mov ss . ecx push es mov es . ecx eld push Offffffffh call _synth_al l--.notes-.off add esp,4 mov ActiveCount,0 btr MpuFlags ,MPU_FLAG_SYNTHON call Synthoff cal l _synth_reinit_voi ces pop es ss esp.spsave jmp NMIEX.it mcwnrs : bt MpuFl ags , MPU_FLAG_UART jne SHORT mewi nt imp NMIExi t mcwi nt : ; handle intelligent mode commands here - ack first mov al ,O eh cal 1 MpuPutData cmp cl,3fh jne SHORT mewver bts MpuFlags , PU_FLAG_UART mcwdoe : bts MpuFlags,MPU_FLAG_DOUT bts MpuFlags,MPU_FLAG_SYNTHON jc NMiExit mov _num_active_voices,20 mov ActiveCouπt,400 call SynthOn jmp NMiExit mewver: cmp cl.Oach jne SHORT mewrev mov MpuTemp.lSh bts MpuFl ags ,MPU_FLAG_DPEND jmp NMiExit mewrev: cmp cl.Oadh jne SHORT mewdxx mov MpuTemp.Olh bts MpuFlags ,MPU_FLAG_DPEND jmp NMiExit mewdxx: and cl.OfOh cmp cl.OdOh 3 e mcwdoe imp NMiExit HOStStatR ABEL NEAR mov BYTE PTR EaxSave,02h jmp NMiExit OdAddrR LABEL NEAR mov al.OdAddr mov BYTE PTR- EaxSave, al jmp NMiExit " OdAddrW LABEL NEAR and cl.Ofh mov OdAddr.cl jmp NMiExit OdDataR LABEL NEAR mov al.OdAddr odr03: cmp al,03h jne SHORT odr04 mov cl ,[esi .ssi_OdieDMA] jmp SHORT odrex odr04: cmp al ,04h jne SHORT odrOβ mov cl , [esi . ssi_Odi eiNT] jmp SHORT odrex odr06: cmp al ,06h jne SHORT odrxx mov cl.OdieCDCfg jmp SHORT odrex odrxx: xor cl.cl odrex: mov BYTE PTR EaxSave, cl jmp NMiExit OdDataW LABEL NEAR mov ah.OdAddr odw03 : cmp ah,03h jne SHORT odw04 mov [esi.ssi_OdieDMA] ,cl jmp NMiExit odw04 : cmp ah,04h jne SHORT odw06 mov [es . ssi_Odi eiNT] , cl jmp NMiExit odw06 : cmp ah,06h jne SHORT odwex mov odieCDCfg.cl odwex: jmp NMiExit AdAddrR LABEL NEAR mov al.AdAddr mov BYTE PTR EaxSave, al jmp NMiExit AdAddrW LABEL NEAR and cl , 7fh test cl,40h jnz SHORT aawms aawmc : ; mode change bit is clear test AdAddr,40h mov AdAddr.cl JZ NMiExit ; new mode change clear btr DacFlags,DAC_FLAG_ADPEND jne NMiExit jmp ADDACStart aawms: ; mode change bit is set test AdAddr,40h mov AdAddr,cl jnz NMiExit ; new mode change enable IFNDEF WIN_CODE call clearPCHRQ ENDIF call DACStOp test AdPinCtl,02h inz NMiExit btr DacFlags ,DAC_FLAG_INTENA jmp NMiExit AdData LABEL NEAR mov al .AdAddr and al ,O h adr06: cmp al,06h jne SHORT adr07 mov cl.AdDACVolL jmp SHORT adrex adr07: cmp al , 07h jne SHORT adrOβ cmp AdDACVo L.Oaah jne SHORT adrrc cmp AdDACVolR,95h jne SHORT adrrc bts DmaFl ags ,DMA_FLAG_MASK adrrc: mov cl.AdDACVolR jmp SHORT adrex adr08: cmp al,08h jne SHORT adr09 mov cl ,AdFormat jmp SHORT adrex adr09: cmp al,09h jne SHORT adrOa mov cl.AdConfig jmp SHORT adrex adrOa: cmp a , Oah jne SHORT adrxx mov cl .AdPi nCtl jmp SHORT adrex adrxx: xor cl . cl adrex: mov BYTE PTR EaxSave.cl jmp NMiExit AdDataW LABEL NEAR mov a , AdAddr and al .Ofh adw02 : cmp al ,02h jne SHORT adw0 mov AdCDVol L. cl mov al .cl shl al , 2 not al and al , 7fh call setcovol jmp NMiExit adw03 : cmp al,03h jne SHORT adw06 mov AdCDVolR.cl jmp NMiExit adw06 : cmp al,06h jne SHORT adw07 and cl.Obfh mov AdDACVolL.cl mov al.cl and al,80h shl c-.l not cl and cl,7fh or al.cl call SetDACVol jmp NMiExit adw07: cmp al,07h jne SHORT adw08 and cl.Obfh mov AdDACVolR.cl jmp NMiExit adw08 : cmp al,08h jne SHORT adw09 test AdAddr, 40h jz NMiExit call DACStOP mov AdFormat.cl mov SblTC,0ffh and [esi .ssi ϊSerFmt] ,0 3h test cl.lOh jz SHORT adwfns or [esi .ssi_bSerFmt] ,04h adwf πs : test cl,40h jz SHORT adwfnb or [esi .ssi_bSerFmt] ,08h adwfnb : mov dx, [esi .ssi_wiOAddressconc add dl ,CONC_bSERFMT_OFF mov al , [esi .ssi_bSerFmt] out dx,al xor edx , edx mov dl.cl and dl.Ofh shl edx. mov ax ,AdFreqTabl e[edx] call DAςsetRate jmp NMiExit adw09 : cmp al,09h jne adwOa test AdAddr,40h jnz adwcm and Adconfig.Ofch and cl,03h or Adconfig.cl test cl.Olh jnz SHORT adwcon call DACPause jmp NMiExit adwcon : bt DacFlags ,DAC_FLAG_RUNNING c NMiExit bt DacFl ags fDAC_FLAG_PAUSE jc SHORT adwcre ADDACStart: bts DacFlags ,DAC_FLAG._AUTO xor eax , eax mov ax,DACCount inc eax test [esi .ssi_bserFmt] ,04h SHORT adwnos eax.l adwnαs : test [esi .ssi_bSerFmt] ,08h SHORT adwnob eax.l adwnob : ih dec eax mov DACCount ,ax call DACStart jmp NMiExit adwcre: call DACResume jmp NMiExit adwcm: mov AdConf g , cl test cl .Olh NMiExit _« DacFl ags , DAC_FLAG__ADPEND IFNDEF WIN. .CODE push ebx mov bl.Olh call DebugCtl pop ebx ENDIF jmp NMiExit adwOa: cmp al ,0ah jne SHORT adwOe mov AdPinCtl.cl btr DacFl ags ,DAC_FLAG_INTENA test cl,02h NMiExit _-5 DacFlags ,DAC_FLAG_INTENA adwOe : cmp al ,0eh jne SHORT adwOf mov BYTE PTR DACCθunt+l,cl jmp NMiExit adwO : cmp al.Ofh jne NMiExit mov BYTE PTR DACCount,Cl jmp NMiExit AdStatUSR LABEL NEAR xor al .al bt PicFlags ,PlC-_FLAG_viRQ jne SHORT asrni or al .Olh asrm mov BYTE PTR EaxSave,al jmp NMiExit AdStatUSW LABEL NEAR IFNDEF WIN_CODE call KillviRQ ELSE call vi rtlRQClear ENDIF imp NMiExit SblMi xAddrW LABEL NEAR mov SblMi xAddr. cl jmp NMiExit Sb .Mi xDataR LABEL NEAR mov al.SblMixAddr get current mixer address cmp al , 30h check for out of bounds jae SHORT smdrx if too high, return NULL value shr eax.l shift out alias bit and eax.OOOOOOlfh mask off garbage mov a ,sblMixer[eax] get mixer data byte mov BYTE PTR EaxSave,al and return it jmp NMiExit smdrx: mov BYTE PTR EaxSave,Offh return NULL jmp NMiExit SblMixDataW LABEL NEAR ;mov ah.SblMi Addr ;mov al ,cl ;call DbPutlδ mov al.SblMixAddr get current mixer address cmp al , 30h check for out of bounds NMiExit if too high , do nothing shr eax,l shift-out al ias bit and eax.OOOOOOlfh mask-off garbage or cl.llh add-i n fi xed bits mov edx , OFFSET Sbl Mi xer point to mixer data mov [edx+eax] , cl store mixer byte cmp al , SB L- .IX-.RES ET jne SHORT smdwse mov al , 99h mov [edx+SBL_MIX_MASTER] ,al mov [edx+SBL_MiX_DAC] ,al mov [edx+SBL_MIX_MIDl],al mov al , llh mov [edx+SBL_MIX_MIC] ,al mov [edx+SBL-j.lX_SOURCE],al mov [edx+SBL_MIX_CD],al mov [edx+SBL_MIX_LlNE],al mov [edx+SBL-j.lX_STEREO] , al call SblSetFormat call sblsetDACVol call SblSetSyπthVol call SblSetCDVol jmp NMiExit smdwse: cmp al,S * BL_MIX_STEREO jne SHORT smdwda and [esi .ssi_bSerFmt] , Of3h test SblStereo,02h jz SHORT smsteO or [esi .ssi_bSerFmt] ,04h smsteO: mov dx , [esi . ssi_wiOAddressconcert] add dl ,CONC_bSERFMT_OFF mov al , [esi . ssi_bSerFmt] out dx.al mov ax.SblByteRate test sblstereo,02h jz SHORT smstel shr ax,l smstel: call DACSetRate jmp NMiExit smdwda : cmp al ,SBL_MIX_DAC jne SHORT smdwsy mov al.cl call sblSetDACVol jmp NMIEXl ' t smdwsy: cmp al,SBL_MIX_MIDI jne SHORT smdwcd mov al.cl call SblSetSynthvol jmp NMiExit smdwcd : cmp al ,SBL_MIX_CD jne NMiExit mov al.cl call SblsetCDVol jmp NMiExit Sbl SetDACVol PROC mov al.SblMixer + SBL_MIX_DAC shr al,3 xor al,63h bt Sbl Flags , SBL_FLAG_SPKRON jc SHORT ssdnm or- al,80h ssdnm: call setDACVol ret SblSetDACVol ENDP SblSetSynthvol PROC mov al.SblMixer + SBL_MIX_MIDI shr al,3 xor al,63h sub al,08h call SetSynthvol ret SblSetSynthvol ENDP SblsetCDVol ROC mov al.SblMixer + SBL_MIX_CD shr a1,3 xor al,63h call setCDVol ret SblsetCDVol ENDP SblSetFormat PROC and [esi .ssi_bSerFmt] ,0f3h test Sblstereo,02h jz SHORT ssfnsO or [esi .ssi_bSerFmt] ,04h ssfnsO: mov dx, [esi .ssi_wlOAddressconcert] add dl,CONC_bSERFMT_OFF mov al , [esi .ssi_bSerFmt] out dx.al mov ax.SblβyteRate test Sblstereo,02h jz SHORT ssfnsl shr ax.l ssfnsl: call DACSetRate ret SblSetFormat ENDP sblResetw LABEL NEAR ;mov ah,06h ;mov al ,cl ;call DbPutlδ test cl.Olh inz SHORT sblresl sblresO: mov al , Oaah call Sbl PutData jmp NMiExit sblresl: btr sbl Fl ags , SBL_FLAG_HISPEED jne SHORT sbl res call DACStop btr DacFl ags ,DAC_FLAG_RUNNING jmp NMiExit sbl res: mov Sbl Proc,OFFSET Sbl Parse mov Sbl Flags, OOOOh call Sbl SetDACVol bts DmaFl ags ,DMA_FLAG_MASK IFNDEF WIN..CODE call ClearPCllRQ call Kill IRQ ELSE call virtlRQClear ENDI srlnos: mov al , [esi .ssi_bserFmt] out dx.al IFNDEF WIN-.CODE push ebx mov bl.Olh call Debugctl pop ebx ENDIF jmp NMiExit SblDataR LABEL NEAR mov al, SblData mov BYTE PTR EaxSave, al btr SblFlags,SBL_FLAGJDPEND jc SHORT sdrmo and Sbl xr . NOT 80h jmp NMiExi t sdr o : mov al , SBL_VER_MINOR call SblPutData jmp NMiExit SblTxrR LABEL NEAR bt DacFl ags ,DAC_FLAG_RUNNING jc SHORT strrun mov BYTE PTR Eaxsave,7fh jmp NMiExit strrun: bt Sbl Flags , SBL_FLAG_HISPEED jc SHORT strhsp mov al , SblToggle or al,7fh add Sblτoggle,20h mov BYTE PTR Eaxsave,al jmp NMiExit strhsp: mov BYTE PTR EaxSave,O fh jmp NMiExit SblCmdW LABEL NEAR ;mov ah.Och ;mov al ,cl ;call DbPutlδ ; -lump to the current SB command process. This will be either the ; command parser or a handler for a command data byte, jmp sbiproc sbl Parse: spcOO: ; First do I/O DAC - gotsta happen fast cmp Cl , SBL_CMD_IODAC jne spcOl mov Sbl PΓOC,OFFSET SblGetlO bts DacFlags ,DAC_FLAG_IODAC JC NMiExit IFNDEF WIN_CODE mov SbllOBuff,80808080h ELSE mov edx, [esi .ssi_lpDMABufferLinear] mov DWORD PTR [edx] , 80808080h ENDIF mov ax,44100 call DACSetRate mov SblTC.Offh mov dx, [esi .ssi_wiOAddressConcert] add dl , CONC_bMEMPAGE_OFF mov al CONC_DACCTL_PAGE out dx.al add dl,CONC_dDACPADDR_OFF - CONC_bMEMPAGE_OFF IFNDEF WIN_CODE mov eax.XMSPaddrLo add eax,OFFSET SbliOBuff ELSE mov eax, [esi .ssi_lpDMABufferPhys] ENDIF out dx.eax add dl.CONC_WDACFC--.OFF - CONC_dDACPADDR_OFF xor eax , eax out dx , eax add dl,CONC_wDACIC_OFF - CONC_wDACFC_OFF mov ax.Offf h out dx.ax add dl ,CONC_bSERFMT_OFF CONC_wDACIC_OFF and [esi .ssi_bSerFmt] ,0f7h mov al , [esi .ssi_bserFmt] out dx.al add dl,CONC_bSERCTL_OFF - CONC_bSERFMT_OFF and [esi . ssi_bSerctl ] , NOT 52h mov al , [esi .ssi_bserctl] out dx.al add dl,CONC_bDEVCTL_OFF - CONC_bSERCTL_OFF or [esi . ssi_bDevctl ] , 20h mov al , [esi . ssi jDevctl ] out dx .al jmp NMiExit spcOl: ; Next , check for hi -speed si ngl e mode DAC commands cmp cl , SBL_CMD_SDACH jne SHORT spcOla btS Sbl FlagS , SBL_FLAG_HISPEED cal l SblCl earSHH jmp Sbl SDACH spcOla: cmp Cl , SBL_CMD_SADCH jne SHORT spc02 bts DacFl ags ,DAC_FLAG_ADC btS Sbl FlagS , SBL_FLAG_HISPEED call SblSetSHH jmp SblSDACH spc02 : ; Next, check for lo-speed single mode DAC cmp cl,SBL--.CMD_SDAC jne SHORT spc02a call SblClearSHH mov Sbl Proc,OFFSET SblGetDacL jmp NMiExit spc02a: Cmp cl,SBL_CMD_SADC jne SHORT spc02b call SblSetSHH bts DacFl ags , DAC_FLAG_ADC mov Sbl Proc,OFFSET SblGetDacL jmp NMiExit spc02b : cmp cl,SBL_CMD_SILENCE jne SHORT spc02c btr DmaFlags ,DMA_FLAG_AUTO mov DmaAddr , OOOOh mov DmaAddrCurr, OOOOh mov DmaCount , Offffh mov DmaCouπtCurr.Offffh btr DmaFlags,DMA_FLAG_MASK call SblSetSHH mov Sbl Proc,OFFSET SblGetDacL jmp NMiExit spc02c: cmp cl,SBL_CMD_SADPCM jne SHORT spc02d call SblSetSHH mov Sbl,Proc,OFFSET SblGetDacL jmp NMiExit spc02d : cmp cl , SBL_CMD_SADPCMR jne SHORT spc03 call SblSetSHH mov sbl Proc,OFFSET SblGetDacL jmp NMiExit spc03 : ; Next, check for the TConst command cmp cl,SBL_CMD_SETTC jne SHORT spc04 mov Sbl Proc,OFFSET SblGetTC jmp NMiExit spc04 : ; Next, check for a halt cmp cl , SBL_CMD_HALT jne SHORT spc05 call DACPause jmp NMiExit spc05: ; Next, check for auto stop cmp cl,SBL_CMD_ASTOP jne SHORT spc06 btr DacFl ags ,DAC_FLAG_AUTO mov dx, [esi .ssi_wlOAddressConcert] add dl ,CONC_bSERCTL_OFF or [esi . ssi_bserctl ] ,40h mov al , [esi . ssi_bSerCtl] out dx.al jmp NMiExit spc06: ; Next, check for a resume cmp Cl,SBL_CMD_RESUME jne SHORT SPC07 call DACResume jmp NMiExit spc07: ; Next, Check for auto block cmp cl , SBL_CMD_SETAC jne SHORT Spc08 mov Sbl Proc,OFFSET SblGetBlkL jmp NMiExit spc08: ; Next, check for hi-speed/lo-speed auto DAC cmp cl,SBL_CMD_-ADACH jne SHORT spcOδa bts Sbl Fl ags , SBL_FLAG_HISPEED call SblClearSHH jmp SHORT spauto spc08a: cmp Cl , SBL_CMD_ADAC jne SHORT spcOδb call SblClearSHH jmp SHORT spauto spcOδb: cmp Cl , SBL_CMD_AADPCM jne SHORT spc08c call SblClearSHH jmp SHORT spauto spc08c: cmp Cl , SBL_CMD_AADCH jne SHORT spcOβd bts sbl Flags,SBL_FLAG_HISPEED call SblSetSHH jmp SHORT spauto spc08d: cmp Cl , SBL_CMD_AADC jne SHORT SPC09 call SblSetSHH spauto: bts DacFlags ,DAC_FLAG_AUTO bts DacFlags ,DAC_FLAG_INTENA mov dx, [esi .ssi_wiOAddressConcert] add dl ,CONC_bSERFMT_OFF and [es .ssι_bSerFmt] ,NOT 08h mov al , [esi .ssi_bSerFmt] out dx.al call DACStart jmp NMiExi t spc09: ; Next, check for forced int command cmp Cl , SBL_CMD_FORCEINT 2 ne SHORT spcOa bts DacFlags ,DAC_FLAG_INTENA IFNDEF WIN_ .CODE call AssertwavelRQ ELSE call vi rtiRQSet ENDIF jmp NMiExit spcOa: ; Next, check fop the scrambler cmp cl , SBL_CMD_SCRAMBLE jne SHORT spcOb mov sbl Proc,OFFSET SblGetScr jmp NMiExit spcOb: ; After this, the cmd eval order isn't all that important cmp cl , SBL_CMD_DSPVER jne SHORT spcOc mov al,SBL_VER_MA30R call sblPutoata bts SblFlagS,SBL_FLAG_DPEND jmp NMiExit spcOc: cmp Cl , SBL_CMD_SETTAG jne SHORT spcOd mov Sbl Proc,OFFSET SblSetTag jmp NMiExit spcOd: cmp Cl , SBL_CMD_GETTAG jne SHORT spcOe mov al, SblTag cal 1 Sbl PutData jmp NMiExit spcOe: cmp cl,SBL_CMD_INVERT jne SHORT spcOf mov Sbl Proc,OFFSET SblInvert jmp NMiExit spcOf : cmp cl,SBL_CMD_SPKRON jne SHORT spclO btS SblFlagS,SBL_FLAG_SPKRON call SblSetDACVol jmp NMiExit spclO: cmp Cl , SBL_CMD_SP ROFF jne SHORT spell btr SblFlags,SBL_FLAG_SPKRON cal 1 Sbl SetDACVol jmp NMiExit spell: cmp cl,SBL_CMD_GETSPKR jne SHORT spcl2 xor al.al bt SblFlagS,SBL_FLAG_SP RON jne SHORT sgsoff dec al sgsoff cal 1 Sbl PutData jmp NMiExit spcl2: jmp NMiExit SblGetlO mov Sb]Proc,OFFSET sbl Parse mov ch.cl mov eax , ecx shl eax, 16 mov ax, ex IFNDEF WIN. .CODE mov SbllOBuff.eax ELSE mov edx, [esi .ssi_lpDMABufferL mov [edx] ,eax ENDIF jmp NMiExit SblGetDacL: mov Sbl Proc,OFFSET sblGetDacu mov BYTE PTR DACCount, Cl jmp NMiExit SblGetDacU: mov Sbl Proc,OFFSET SblParse mov BYTE PTR DACCOUnt+l.cl SblSDACH: btr DacFl ags ,DAC_FLAG_AUTO bts DacFl ags , DAC_FLAG_INTENA btr DacFl ags , DAC_FLAG_ADC SHORT sgdoit _ .< DmaFl ags ,DMA_FLAG_MASK jc SHORT sgdoit xor eax , eax mov dx, [esi .ssi_DMAPageReg] in al ,dx shl eax , 16 mov ax,DmaAddrCurr mov edx , eax mov al , 80h mov ecx.l call writePhys sgdoit: mov dx, [esi .ssi_wiθAddressConcert] add dl ,CONC_bSERFMT_OFF and [esi . ssi_bSerFmt] .NOT 08h mov al , [esi . ssi.bSerFmt] out dx.al call DACStart jmp NMiExit sblGetTC: mov Sbl proc, OFFSET SblParse ; if it's already set, get off here cmp cl . sblTC je NMIExi t ; store new time const mov SblTC. cl ; derive rate divider not cl and ecx.OOOOOOffh inc ecx mov eax, 1000000 xor edx , edx div ecx mov SblByteRate , ax call SblSetFormat jmp NMiExit SblGetBlkL: mov Sbl Proc,OFFSET SblGetBlku mov BYTE PTR DACCount , Cl jmp NMiExit SblGetBl u: mov Sbl Proc, OFFSET SblParse mov BYTE PTR DACCount+l. cl jmp NMiExi t SblGetScr: mov bl Proc, OFFSET Sbl Parse xor cl . SblxorPat add cl .SblAccum mov SblAccum. cl ror Sblxorpat,2 xor eax , eax mov dx, [es .ssi_DMAPageReg] in al.dx shl eax , 16 mov ax,DmaAddrCurr mov edx , eax mov al.cl mov ecx.l call writePhys inc DmaAddrCurr dec DmaCountCurr set terminal condition mov al , [es . ssi_DmaTCMask] or DmaStatus .al jmp NMiExit SblSetTag: mov sbl Proc, OFFSET SblParse mov sbl Tag, cl jmp NMiExit sbl invert: mov sbl Proc,OFFSET SblParse not cl mov al , cl call sbl PutData jmp NMiExit SblRXTR LABEL NEAR ;bt PicFlags,PIC_FLAG_VISR ;jnc SHORT nostat ;mov ax,22eh ;call DbPutlδ nostat: IFNDEF WIN_CODE call Kill iRQ ELSE call virtiRQClear ENDIF srrex: mov al.SblRxr mov BYTE PTR EaxSave, al jmp NMiExit IFNDEF WIN_CODE ; Master controller data read MicDataR LABEL NEAR cmp Mi cCmd , Oah jne SHORT mdrisr Bt PicFlagS,PIC_FLAG_VIRR jne NMiExit jmp SHORT mdrvim mdrisr: bt PicFlags,PlC_FLAG_VlSR jne NMiExit mdrvim: cmp [esi .ssi_biRQ] ,08h jae SHORT mdrvms or cl .waveiRQMask mov BYTE PTR EaxSave.cl jmp NMiExit mdrvms: or cl,04h mov BYTE PTR EaxSave, cl jmp NMiExit ; Master controller command write MicCmdW LABEL NEAR of any sort - if so, go to EOI sequence ; if it's the init command byte, flag mask-write and get out test cl.lOh jz mcwconO mov Micskip,3 jmp NMiExit mcwconO : ; check for reg-cead commands that we need to store mov al , cl and al .Ofeh cmp al .Oah jne NMiExi t mov Mi cCmd , cl jmp NMiExi t ; here we got an EOI; there are several ways to deal with this mcwEOI : ; see i f we need to wai t for the l ast EOI bt Pi cFl ags , PiC_FLAG_WAiτ jne mcwcon2 here we ' re waiti ng for the l ast EOI mov al . Obh get i n-servi ce reg out 20h , al in al , 21h in al , 20h mov ah .al mov al , Mi cCmd restore last reg-read command out 20h ,al or ah , ah i f any phys i n-servi ce , wait for next jnz NMiExit ; last EOI in the chain - we need to see if it was a real -mode EOI btr Pi CFI ags , PIC_FLAG_WAΓT mov eax.ss lar eax, eax test eax,00400000h jnz SHORT mcwsok and esp.OOOOffffh mcwsok: test DWORD PTR [esp+SAVE_FRAME+8],20000h jnz mcwdi s ; not a v86 EOI - re-assert PCI IRQ to try again ... ; apw ; mov eax.Offffffffh ; call DbPut32 and DWORD PTR [esp+SAVE_FRAME+8] ,NOT 00000200h call ForcePCiiRQ jmp NMiExit mcwdi s : ; it was real -mode, whack the v86 return stack to dispatch service mov RegSave , es mov eax , cs add eax.lOh mov es , eax mov eax, [esp+SAVE_FRAME+12] and eax, OOOOffffh ; grow v86 stack by an iret frame sub eax, 6 mov [esp+SAVE_FRAME+12] ,eax mov edx, [esp+SAVE_FRAME+16] and eφc, OOOOffffh create linear pointer to new stack top shl edx, 4 add edx , eax mov eax, [esp+SAVE_FRAME+0] mov es: [edx ,ax copy return frame to new stack area mov eax, [esp+SAVE_FRAME+4] mov es: [edx+2] ,ax mov eax, [esp+SAVE_FRAME+8] mov es: [edx+4] ,ax xor eax , eax mod return frame to IRQ vect mov al .wavelRQVector shl eax, 2 mov eax.es: [eax] mov [esp+SAVE_FRAME+0] ,ax shr eax , 16 mov [esp+SAVE_FRAME+4] ,ax mov al.Offh out 21h,al bts pi cFl ags , PIC_FLAG_VISR mov es.RegSave jmp NMIEXIt mcwcon2 : this could be the EOI from the dispatched handler btr pi cFl ags , PIC_FLAG_VISR jne SHORT mcwcoπ3 mov al .MicMask out 21h,al call DisablevirtualPlC jmp NMiExit mcwcon3 : ; this could be a spec EOI w/ the IRQ masked - if so kill VIRQ/VIRR cmp [esi.ssi_blRQ],08h jae NMiExit cmp cl .waveSpecEOl jne NMiExit call clearPCHRQ call Kill IRQ jmp NMIExi-t ; Master controller mask read MicMaskR LABEL NEAR bt Pi cFl ags , PIC_FLAG_VISR jne NMiExit cmp [esi . ssi_blRQ] , 08h jae mmrisl mov al, M cMask mov ah.waveiRQMask not ah or al.ah mov BYTE PTR EaxSave,al jmp NMiExit mmrisl mov al , Of f h mov BYTE PTR EaxSave,al jmp NMiExit ; Master controller mask write MicMaskW LABEL NEAR ; if it's an init cmd word, ski cmp MicSkip.O je SHORT mmwconO dec MicSkip jmp NMiExit mmwconO: bt pieFl ags , PIC_FLAG_VISR if unmasking is allowed jne SHORT mmwnrm skip to normal sequence mov al.Offh else, remask master PIC out 21h,al cmp [esi .ssi_blRQ] ,08h jae NMiExit mov al ,waveiRQMask apply only wave IRQ mask bit not al and MicMask.al and cl .waveiRQMask or MicMask.cl jmp NMiExit mmwnrm: mov MicMask.cl ; save mask state bt Pi cFl ags , PIC_FLAG_PEND ; got a request pending? jne NMiExit ; if not, we're done cmp [esi.ssi_biRQ],08h are we on the master PIC? jae SHORT mmwcas if not, skip to cascade sequence test cl .waveiRQMask is our IRQ unmasked? jnz NMiExit if not, we're done btr p cFlags,PlC_FLAG_PEND clear pending status call ForcePCllRQ force the interrupt jmp NMiExit mmwcas : test cl,04h is cascade IRQ unmasked? jnz NMIEXIt if not, we're done mov al.SicMask get slave controller mask state test al , waveiRQMask is our IRQ unmasked? jnz NMiExit if not, we' re done btr Pi cFlags , PIC_FLAG_PEND clear pending status call ForcePCllRQ force the interrupt jmp NMiExit ; slave controller data read SicDataR LABEL NEAR cmp [esi.ssi_blRQ],08h if we're not on slave, don't bother jb NMIEXIt cmp siccmd.Oah SHORT sdrisr j; e pi cFl ags , PIC_FLAG_VIRR jne NMiExit mp SHORT sdrvim sdrisr: bt pi cFl ags , PIC_FLAG_VISR jne NMiExit sdrvim: or cl .waveiRQMask mov BYTE PTR EaxSave, cl jmp NMIEXIt ; slave controller command write SicCmdW LABEL NEAR ; if it's the init command byte, flag mask-write and get out test cl.lOh jz SHORT scwconO mov SicSkip,3 jmp NMiExit scwconO: cmp [esi .ssi_blRQ] ,08h if we're not on slave, don't bother jb NMiExit check for reg- read commands that we need to store mov al,cl and al.Ofeh cmp al ,0ah jne SHORT scwconl mov Si cCmd , cl jmp NMiExit scwconl: " ; this could be a spec cmp cl .waveSpecEOl jne NMiExit call Kill IRQ jmp NMIEXit ; Slave controller mask write SicMaskW LABEL NEAR ; if it's an init cmd word, skip it cmp SicSkip.O je SHORT smwconO dec SicSkip jmp NMiExit smwconO: cmp [esi .ssi_blRQ] ,08h if we're not on slave, don't bother jb NMiExi t mov Si cMask. cl save mask state bt Pi cFl ags , PIC_FLAG_PEND got a request pending? jne NMiExit if not, we're done test cl .waveiRQMask is our IRQ unmasked? jnz NMiExit if not, we're done test Mi cMask, 04h is the master PIC cascade unmasked? jnz NMiExi t if not, we' re done btr Pi cFl ags , PIC_FLAG_PEND clear pending status cal l ForcePCllRQ force the interrupt jmp NMiExit ENDIF DmaMOdeW LABEL NEAR dmwaon : bts DmaFlags ,DMA_FLAG_AUTO jmp Nmi Exit DmaMaskW LABEL NEAR mov al , cl and al,03h cmp al , [esi . ssi_bDMA] jne NMiExit test cl,04h έ ' nz SHORT dmwsm tr DmaFl ags , DMA_FLAG_MASK jne NMiExi t btr DmaFl ags , DMA_FLAG_DRQ jne NMiExit btr DacFlags, DAC_FLAG_PEND jne SHORT dmres cal DACGoDa mit jmp NMiExit dmres: cal 1 DACResume jmp NMiExit dmwsm: bts DmaFl ags , DMA_FLAG_MASK JC NMiExit bt DacFlags,DAC_FLAG_RUNNING jne NMiExit call DACPause bts DmaFl ags , DMA_FLAG_DRQ jmp NMiExit DmaF opW LABEL NEAR btr DmaFl ags , DMA_FLAG_FLOP jmp NMiExit DmaOAddrR LABEL NEAR btc DmaFl ags , DMA_FLAG_FLOP cmp [esi .ssi_bDMA] ,0 je DMAAddrR jmp NMiExit DmalAddrR LABEL NEAR btc DmaFl ags , DMA_FLAG_FLOP cmp [esi . ssi_bDMA] , l DMAAddrR mp NMiExit Dma3AddrR LABEL NEAR btc DmaFlags , DMA_FLAG_FLOP cmp [esi .ssi_bDMA] ,3 DMAAddrR jm e p NMIEXIt DmaAddΓRii::: bt DmaFl ags ,DMA_FLAG_FLOP jc SHORT darlow mov cl.BYTE PTR DmaAddrCurr+1 mov BYTE PTR EaxSave, cl jmp NMiExit darlow: bt DacFl ags ,DAC_FLAG_RUNNING SHORT darder St C DacFl ags ,DAC_FLAG_PAUSE SHORT darder jmp SHORT darget darder: mov ex,DmaAddr cal l cal l dastor: mov DmaAddrCurr, ex darget: mov ax,DmaAddrCurr mov BYTE PTR EaxSave, al jmp NMiExit DmaOAddrW LABEL NEAR btc DmaFlags ,DMA_FLAG_FLOP cmp [esi .ssi_bDMA] ,0 , e DMAAddrw mp NMiExit DmalAddrw LABEL NEAR btc DmaFlags, DMA_FLAG_FLOP cmp [esi .ssi_bDMA],l je DMAAddrw jmp NMiExit Dma3Addrw LABEL NEAR btc DmaFl ags,DMA_FLAG_FLOP cmp [esi .ssi_bDMA] ,3 je DMAAddrw mp NMiExit DMAAddrw: bt DmaFlags.DMA_FLAG_FLOP jne SHORT dawupp mov BYTE PTR DmaAddr.cl mov BYTE PTR DmaAddrCurr.cl jmp NMiExit dawupp : mov BYTE PTR DmaAddr+l.cl mov BYTE PTR DmaAddrCurr+l.cl jmp NMiExit DmaOCOUntR LABE NEAR btc DmaFl ags, DMA_FLAG_FLOP cmp [esi .ssi_bDMA],0 je DMACountR mp NMiExit DmalCountR LABEL NEAR btc DmaFl ags,DMA_FLAG_FLOP cmp [esi .ssi_bDMA],l je DMACountR jmp NMiExit Dma3CθuntR LABEL NEAR btC DmaFlags, DMA_FLAG_FLOP cmp [esi . ssi_bDMA] , 3 je DMACountR jmp NMIExit DmaCountR: bt DmaFl ags , DMA_FLAG_FLOP jc SHORT dcrlow mov cl.BYTE PTR DmaCountCurr+1 mov BYTE PTR EaxSave, cl jmp NMiExit dcrlow: bt DacFlags ,DAC_FLAG_RUNNING jc SHORT dcrder bt DacFlags,DAC_FLAG_PAUSE jc SHORT dcrder jmp SHORT dcrget dcrder: xor eax, eax xor ecx, ecx mov cx.DmaCount mov dx, [esi .ssi_wlOAddressConcert] add dl , CONC_bMEMPAGE_OFF mov al , CONC_DACCTL_PAGE out dx.al add dl , CONC_WDACCFC_OFF - CONC_bMEMPAGE_OFF call DisableβusMaster in ax.dx call EnableBusMaster shl eax, 2 sub ecx, eax inc SHORT dcrsto Bt DmaFlags , DMA_FLAG_AUTO jne SHORT dcrsing add cx.DmaCount jmp SHORT dcrsto dcrsing: mov cx.Offffh dcrsto: mov DmaCountCurr.cx dcrget: mov ax.D aCountCurr mov BYTE PTR EaxSave, al jmp NMiExit DmaOCountW LABEL NEAR btc DmaFl ags , DMA_FLAG_FLOP cmp [esi .ssi_bDMA] ,0 je DMACouπtw jmp NMiExit Dmalcountw LABEL NEAR btc DmaFlags , DMA_FLAG_FLOP cmp [esi .ssi_bDMA],l DMACountw mp NMiExit Dma3CountW LABEL NEAR btc DmaFlags , DMA_FLAG_FLOP cmp [esi .ssi_bDMA],3 DMACountw mp NMiExit DMACountw: bt DmaFlags , DMA_FLAG_FLOP jne SHORT dewupp mov BYTE PTR DmaCount.cl mov BYTE PTR DmacountCurr.cl jmp NMiExit dewupp: mov BYTE PTR Dmacount+l.cl mov BYTE PTR DmaCountCurr+l.cl btr DmaFlags ,DMA_FLAG_SCOUNT jmp NMiExit DmastatusR LABEL NEAR or cl.Dmastatus bt DmaFl ags , DMA_FLAG_DRQ jne SHORT srnop or cl , [esi . ssi_DmaDRQMask] dsrnop: mov BYTE PTR EaxSave, cl mov al , [esi .ssi_DmaTCMask] not al and DmaStatus.al jmp NMiExit oplstatR LABEL NEAR mov al.OplStat mov BYTE PTR EaxSave, al btr MpuFl ags , PU_FLAG_FMDET nc SHORT osrex bts MpuFl ags ,MPU_FLAG_SYNTHON jc SHORT osrex mov _num_active_voi ces , 12 mov ActiveCount,400 call Synthon osrex: jmp NMiExit oplAddrw LABEL NEAR mov OplAddr . cl jmp NMiExi t oplDataw LABEL NEAR xor edx , edx OPL data reg write mov dl .OplAddr mov _fm_array-Ledxj ,cl ; fi l l -i n FM data array cmp dl.Olh je SHORT oplenq mov dh.dl and dh.OfOh cmp dh.ObOh ; need to process FM notes? jne SHORT fmtdet oplenq: mov ActiveCount,400 bts MpuFl ags , MPU_FLAG_SYNTHON jc SHORT mcall mov _num_active_voi ces , 12 push edx call SynthOn pop edx fmeal 1 : mov eax.ds mov SSoffset.esp ; switch to local stack mov SSselector.ss mov esp,OFFSET stackTop mov ss.eax push es mov es , eax eld push edx call _fm_process add esp,4 pop es Iss esp.SpSave jmp NMiExit fmtdet: cmp Op!Addr, 4 test for timer detection jne NMiExit fmdtl: test Cl ,T1_TRIG jz SHORT fmdt2 or OplStat,Tl_IRQ OR FM_IRQ bts MpuFlags ,MPU_FLAG_FMDET fmdt2 : test Cl ,T2_TRIG jz SHORT fmdml or OplStat,T2_IRQ OR FM_IRQ bts MpuFl ags ,MPU_FLAG_FMDET fmdml: test Cl ,Tl_MASK jz SHORT fmdm2 and OplStat.NOT Tl_IRQ fmdm2 : test Cl ,T2_MAS jz SHORT fmdtr and OplStat.NOT T2_IRQ fmdtr: test OplStat,Tl_IRQ OR T2_IRQ jnz NMiExit and OplStat.NOT FM_IRQ jmp NMiExit iONull LABEL NEAR jmp NMiExit SblSetSHH PROC bt Sbl Fl ags , SBL_FLAG_SPKRON i SHORT SSSOff b π t c s Sbl Fl ags , SBL_FLAG_SHH mov al , DACVol or al,80h call SetDACVol sssoff : ret SblSetSHH ENDP SblClearSHH PROC btr Sbl Fl ags , SBL_FLAG_SHH SHORT SCSOff sbl Flags,SBL_FLAG_SPKRON jne SHORT SCSOff mov al.DACVOl and al,7fh call SetDACVol scsoff: ret SblClearSHH ENDP Sbl PutData PROC mov SblData, al or SblRxr,80h ret Sbl PutData ENDP MpuPutoata PROC mov MpuData.al and MpuStat.NOT 80h ret MpuPutData ENDP IFNDEF WIN_CODE ; this process asserts the virtual wave IRQ AssertwavelRQ PROC bt DacFlags ,DA _FLAG_INTENA jc SHORT awicO ret awi cO : bts Pi CFl ags , PIC_FLAG_VIRQ ; assert vi rtual IRQ jne SHORT awicl ; if already asserted, bail ret awicl: call EnablevirtualPiC bts Pi CFl ags , PIC_FLAG_VIRR ; assert virtual IRR cmp [esi.ssi_blRQ],08h ; are we on the master PIC? jae SHORT awi siv ; if not, skip to slave sequence mov aH,MicMask ; get master Pic mask state test al .waveiRQMask ; is our IRQ masked? jnz SHORT awipnd ; if so, do pend sequence jmp SHORT awi fre ; else, skip to force PCI IRQ awi siv: mov al.SicMask ; get slave PIC mask state test al .waveiRQMask ; is our IRQ masked? jnz SHORT awi pnd ; if so, do pend sequence test MicMask,04h ; is the cascade IRQ masked? jnz SHORT awifre ; if so, do pend sequence jmp SHORT awifre ; esle, force it awi pnd : bts Pi cFl ags , PIC_FLAG_PEND ; flag irq pending ret awi fre: call ForcePCllRQ ; force PCI IRQ ret AssertwaveiRQ ENDP ; this process asserts the physical PCI IRQ ForcePCllRQ PROC mov dx, [esi .ssi_wlOAddressConcert] ; assert PCI IRQ add dl , CONC--.bNMICTL--.OFF or [esi . ssi_bNMictl ] , Olh mov al , [esi . ssi_bNMiCtl ] out dx,al ret ForcePCllRQ ENDP ; this process clears physical PCI IRQ ClearPCHRQ PROC mov dx, [esi .ssi_wiOAddressConcert] ; assert PCI IRQ add dl , CONC_bNMICTL_OFF and [esi . ssi_bNMictl ] , NOT Olh mov al , [esi . ssi_bNMictl] out dx.al ret ClearPCHRQ ENDP ELSE irtlRQSet PROC bts picFlags,PIC_FLAG_VIRQ mov ecx.ds mov ssoffset.esp mov SSselector.ss mov esp,OFFSET stackTop mov ss.ecx push es mov es.ecx eld mov eax , [esi . ssi_dwlRQHandl e] mov ebx, [esi .ssi_dwCODECOwnerCur] vxDCal 1 VPiCD_Set_lnt_Request , pop es lss esp.SpSave ret VirtlRQSet ENDP virtiRQClear PROC btr pfCFlags,PIC_FLAG_VIRQ mov ecx.ds mov ssoffset.esp mov SSselector.ss mov esp,OFFSET StackTop mov ss.ecx push es mov es.ecx eld mov eax, [esi .ssi_dwiRQHandle] mov ebx, [esi .ssi_dwCODECOwnerCur] vxDCall VPiCD_Clear_int_Request pop es lss esp.spSave ret vi rtiRQCl ear ENDP ENDIF ri tePhys PROC IFNDEF WIN_CODE push es mov ebx , cs add ebx,10h mov es.ebx wphyl p: mov es: [edx],al inc edx loop wphylp pop es ELSE mov ebx.ds mov ssoffset.esp mov SSselector.ss mov esp.OFFSET stackTop mov ss.ebx push es mov es.ebx eld call HARDWARE_PhysMemwrite Pop es lss esp.SpSave ENDIF ret writePhys ENDP DACSetRate PROC x 579200/8 x cradj cradj: sub eax, 2 mov dx, [esi .ssi_wlOAddressConcert] add dl,CONC_wDACRATE_OFF and [esi . ssi_wDACRate] .OeOOOh or [esi .ssi_wDACRate] ,ax mov ax, [esi .ssi_wDACRate] out dx.ax ret DACSetRate ENDP Synthon PROC xor eax, eax mov edx , [esi . ssi_l pDMABuf erLi near3] mov ecx, 512 soclr: mov [edx] , eax add edx,4 loop soclr mov dx , [esi . ssi_wIOAddressConcert] add dl,CONC_bMEMPAGE_OFF mov al , CONC_SYNCTL_PAGE out dx.al add dl,CONC_dSYNPADDR_OFF - CONC_bMEMPAGE_OFF mov eax, [esi .ssi_lpDMABufferPhys3] out dx.eax add dl ,CONC_wSYNFC_OFF - CONC_dSYNPADDR_OFF mov eax, 511 out dx,eax add dl ,CONC_wSYNIC_OFF CONC_WSYNFC_OFF mov ax, 255 out dx.ax add dl,CONC_bSERFMT_OFF - CONC_wSYNIC_OFF or [esi . ssi_bSerFmt] ,03h mov al , [esi .ssi_bSerFmt] out dx.al add dl,CONC_bSERCTL_OFF - CONC_bSERFMT_OFF and [esi .ssi_bserctl] .NOT 28h or [esi .ssi_bserctl] ,01h mov al , [esi .ssi_bSerCtl] out dx.al add dl,CONC_bDEVCTL_OFF - CONC_bSERCTL_OFF or [esi .ssi_bDevCtl] ,40h mov al , [esi .ssi_bDevctl] out dx.al IFNDEF WIN_CODE push ebx mov bl , Olh call Debugctl pop ebx ENDIF ret SynthOn ENDP ; kill the synth machine SynthOff PROC mov dx, [esi .ssi_wlOAddressConcert] add dl ,CONC_bDEVCTL_OFF and [esi .ssi_bDevCtl],NOT 40h mov al , [esi .ssi_bDevctl] out dx.al ret SynthOff ENDP ; startup the DAC Cor defer startup if the VDMAC is masked) DACStart PROC ; this is a new DAC startup ... need to kill DAC ? test DacFlags.Cl SHL DAC_FLAG_PAUSE) OR Cl SHL DAC_FLAG_IODAC) J z SHORT dsnokill and. DacFlags.NOT CCl SHL DAC_FLAG_PAUSE) OR Cl SHL DAC_FLAG_IODAC)) mov dx, [esi .ssi_wlOAddressConcert] add dl ,CONC_bDEVCTL_OFF and [e§i.ssi_bDevCtl],NOT 20h mov al , [esi .ssi_bDevCtl] out dx.al dsnokill I: bt DmaFl ags , DMA_FLAG_MASK jc SHORT dsdefer call DACGoDammit ret dsdefer: bts DmaFlags ,DMA_FLAG_DRQ bts DacFl ags , DAC_FLAG_PEND ret DACStart ENDP DACGoDammi t PROC ; clear pending flags btr DacFl ags , DAC_FLAG_PEND btr DmaFl ags , DMA_FLAG_DRQ ; derive the ski p counts mov al , 08h test [esi .ssi_bSerFmt] ,08h jz SHORT dgnotlδ shl eax.l dgnotlβ : mov Normal Ski p.al mov al.BYTE PTR DmaAddr and al,03h mov startski p . al mov al ,03h sub al.startskip sub al , BYTE PTR DmaCount and al,03h add al . startski p mov Ext raBy tes , al shl eax , 3 add al .Normal Skip mov EndSkip.al ; derive some .other count varibales xor eax, eax xor ecx, ecx xor edx, edx mov ax,DmaCount mov ex,DACCount mov dl .ExtraBytes inc eax inc ecx add edx , eax test [es . ssi_bSerFmt] ,04h jz SHORT dgnots shr eax.l shr ecx.l dgnots : test [esi .ssi_bSerFmt] ,08h jz SHORT dgnotw shr eax.l shr ecx.l dgnotw: or eax, eax jz SHORT dgnodO dec eax dgnodO : mov DMACountSampl es , ax or ecx, ecx jz SHORT dgnodl dec ecx dgnodl: mov DACCountsamples , ex shr edx, 2 or edx, edx jz SHORT dgnod2 dec edx dgnod2 : mov DMACountDWords.dx ; Now, see if we have to stop an already-running DAC bt DacFl ags , DAC_FLAG_RUNNING jne SHORT dgdostart mov dx, [esi ,ssi_wIOAddressConcert] add dl,CONC_bDEVCTL_OFF and [esi.ssi_bθevCtl] .NOT 20h mov al , [esi . ssi_bDevCtl ] out dx.al ; test for special case - was running, got new DAC count of ZERO cmp DACCountsampl es , OOOOh jne SHORT dgdostart btr DacFl ags ,-DAC_FLAG_RUNNING IFNDEF WIN_CODE call AssertwavelRQ ELSE call VirtlRQSet ENDIF ret dgdostart: ; Fi nally we ' re starting thi s thing - fi rst fl ag it bts DacFl ags , DAC_FLAG_RUNNING ; setup the upper portion of the 32-bit host address ; get our I/O address, pause, and write DMA addr mov dx, [esi .ssi_wiOAddressConcert] mov ecx ,400 call DisableBusMaster dgwait : - CONC_bMEMPAGE_OFF ; program DMA count Cif necessary) add dl,CONC_WDACFC_OFF - CONC_dDACPADDR_OFF btr DmaFlags,DMA_FLAG_SCOUNT jc SHORT dgdcw xor eax, eax mov ax.DMACountDWords out dx.eax dgdcw: ; now test the DAC mode and go to the proper startup sequence bt DacFl ags , DAC_FLAG_AUTO It DGAUtO DmaFlags ,DMA_FLAG_AUTO jc DGSimAuto This i s a real single-mode startup - single DAC, single DMA DGSingle mov iperβuff.Offffh xor eax,eax xor ecx, ecx mov ax , DmaCountCurr mov ex ,DACCount inc ecx sub eax, ecx jne SHORT dgsmOO mov iperBuff, OOOOh cmp ax,O fffh je SHORT dgsmOO mov ax,Offffh btr DacFl ags ,DAC_FLAG_INTENA dgsmOO: mov DmaTermCount.ax mov cx.DmaCountCurr sub ecx, eax add ex,DmaAddrCurr mov DmaTermAddr , ex add dl,CONC_wDACIC_OFF - CONC_wDACFC_OFF mov ax,DACCountsamples out dx.ax dgrips: ; si πgl e mode -".let it rip mov dx , [esi . ssi_wIOAddressConcert] add dl , CONC_bSKIPC_OFF mov al . startski p out dx.al add dl,CONC_bSERCTL_OFF - CONC_bSKIPC_OFF and [esi.ssi_bSerCtl],NOT lOh or [esi .ssi_bSerCtl] ,42h mov al , [esi ,ssi_bSerCtl] out dx,al add dl,CONC_bDEVCTL_OFF - CONC_bSERCTL_OFF or [esi . ssi_bDevCtl ] , 20h mov al , [esi . ssi_bDevCtl] out dx .al ret ; Thi s i s simul ated auto-mode startup - si ngl e DAC, auto DMA DGS mAuto: mov ax,DACCount cmp ax ,DmaCount ja dgsa02 dgsaOO: ; this would be the "normal" sim-auto case DAC is <= DMA count bts DmaFlags ,DMA_FLAG_SCOUNT mov iPerDisp.O mov iPerDispCurr.O mov iperβuff,Offffh xor eax, eax xor ecx, ecx mov ax.DmacountCurr mov ex,DACCount inc ecx sub eax, ecx jne SHORT dgsaOl mov iperBuff.O add ax,DmaCount inc eax dgsaOl: mov DmaTermCount.ax mov cx.DmaCountCurr sub ecx, eax add ex,DmaAddrCurr mov DmaTermAddr , ex add dl ,CONC_WDACIC_OFF - CONC_WDACFC_OFF mov ax , DACCountsampl es out dx,ax jmp dgrips ; sim-auto, big DAC dgsa02: mov ax ,DmaCount mov DmaTermcount,ax mov ax,DmaAddr mov DmaTermAddr , ax cmp Extraβytes.O jne dgSa03 ; sim-auto, big DAC, aligned mov IPerDisp.O mov IPerDispCurr.O mov iPerβuff.O add dl , CONC_wDACIC_OFF CONC_WDACFC_OFF mov ax,DACCountsamples out dx.ax jmp dgrips dgsa03: ; sim-auto, big DAC, unaligned xor eax, eax xor edx, edx xor ecx, ecx mov ax ,DACCount inc eax mov cx.DmaCount inc ecx div ecx dec eax mov perDisp.ax mov iperDispCurr.ax mov iPerβuff.O mov dx, [esi . ssi_wiOAddressConcert] add dl ,CONC_WDACIC_OFF mov ax,DMACountSamples out dx.ax jmp dgripa ; This is real auto-mode startup - aauutto DAC, auto DMA DGAuto: ; two sub-cases arise here ... DACCount <- DmaCount or >. ; if > is the case, weird alignment will need special treatment mov ax,DACCount cmp ax ,DmaCount jbe dgamOl ; Auto Big DAC case here ; check for weird alignment cmp ExtraBytes.OOn jne SHORT dgamOO ; this i s the Auto Big DAC, normal case mov iperβuff,0 mov lperβuffCurr,0 mov ipero sp.O mov iperDispCurr.O add dl,CONC_WDACIC_OFF - CONC_WDACFC_OFF mov ax ,DACCountsamples out dx.ax dgamOO: ; this is the big DAC, unaligned case - must int per DMA buff mov perBuff.O mov iperBuffCurr.O xor eax , eax xor ecx,ecx xor edx, edx mov ax,DACCount inc eax mov cx.DmaCount inc ecx div ecx dec eax mov iperoisp.ax mov IperDispCurr.ax mov dx, [es .ssi_wlOAddressConcert] add dl ,CONC_WDACIC_OFF mov ax ,DMACountSamples out dx.ax jmp SHORT dgripa dgamOl: ; small DAC ... derive iπts per buff xor eax,eax xor ecx,ecx xor edx,edx mov ax,DmaCount inc eax mov ex,DACCount inc ecx div ecx dec eax mov Iperβuff.ax mov iperβuffCurr.ax mov iperDisprO mov iperDispCurr.O ; program the interrupt count mov dx, [esi .ssi_wlOAddressConcert] add dl,CONC_WDACIC_OFF mov ax,DACCountsamples out dx,ax dgri pa: ; auto mode - let it rip mov dx , [esi . ssi_wlOAddressConcert] add dl , CONC_bSKIPC_OFF mov al , Normal Ski p cmp iperBuff. O jne SHORT dgnoes mov al . EndSki p dgnoes : or al . startski p out dx.al add dl,CONC_bSERCTL_OFF - CONC_bS IPC_OFF and [esi.ssi_bSerCtl],NOT 50h or [esi .ssi_bSerCtl] ,02h mov al,[esi .ssi_bSerCtl] out dx.al add dl,CONC_bDEVCTL_OFF - CONC_bSERCTL_OFF or [esi . ssi_bDevCtl ] , 20h mov al , [esi . ssi_bDevctl ] out dx.al ret DACGoDammit ENDP DACStop PROC mov DacFlags,OOOOh OR Cl SHL DAC_FLAG_INTENA) mov dx, [esi .ssi_wiθAddressCoπcert] add dl,CONC_bSERFMT_OFF and [esi.ssi_bSerFmt],NOT 40h mov al , [esi .ssi_bSerFmt] out dx,al add dl,CONC_bDEVCTL_OFF - CONC_bSERFMT_OFF and [esi .ssi_bDevCtl],NOT 20h mov al , [esi . ssi_bDevctl ] out dx.al ret DACStop ENDP DACPause PROC btr DmaFlags, DMA_FLAG_DRQ btr DacFl ags,DAC_FLAG_PEND bt DacFl ags , DAC_FLAG_RUNNING inc SHORT dpexit bt DacFlags,DAC_FLAG_PAUSE jc SHORT dpexit mov dx, [esi .ssi_wlOAddressconcert] add dl,CONC_wDACCIC_OFF call DisableβusMaster in ax , dx call EnableBusMaster cmp ax,OOOlh ja SHORT dpdoit mov ecx, lOOOh call DisableBusMaster dpwz: in ax.dx or ax, ax loopnz SHORT dpwz call EnableBusMaster mov ecx , lOOOh call DisableBusMaster dpwnz: in ax.dx or ax, ax loopz SHORT dpwnz call EnableBusMaster bt DacFlags,DA _FLAG^.UTO jne SHORT dpexit dpdoit: btr DacFl ags,DAC_FLAG_RUNNING bts DacFlags ,DAC_FLAG_PAUSE add dl.CONC_bSERCTL--.OFF - CONC_WDACCIC_OFF or [esi . ssi_bserctl ] , lOh mov al, [esi .ssi_bserctl] out dx.al dpexit: ret DACPause ENDP DACResume PROC bt DacFl ags ,DAC_FLAG_PAUSE SHORT drexit DmaFlags ,DMA_FLAG_MAS SHORT drdoit _.s C DmaFl ags ,DMA_FLAG_DRQ ret drdoit: btr DmaFlags ,DMA_FLAG_DRQ btr DacFlags , DAC_FLAG_PAUSE bts DacFlags ,DAC_FLAG_RUNNING mov dx, [esi .ssi_wiOAddressConcert] add dl ,CONC_bSERCTL_OFF and [esi.ssi_bSerctl],NOT lOh or [esi .ssi_bSerctl] ,02h mov al , [esi .ssi_bSerCtl] out dx.al drexit: ret DACResume ENDP SetDACVol PROC mov DACVol , al mov ah.al and ah.δOh and al,7fh shr aj,2 cmp al,06h jb SHORT sdvok sub al.Oβh sdvok: not al and al.lfh or al,ah mov ah,02h call CODECWrite mov ah,03h call CODECWrite ret SetDACVol ENDP setMastervol PROC mov Mastervol.al mov ah.al and ah.δOh and al,7fh shr al,2 not al and al.lfh or al ,ah mov ah , OOh call CODECWrite mov ah,Olh call CODECWri te ret SetMastervol ENDP SetSynthvol PROC and al,7fh mov Synthvol.al shr al,2 cmp al,06h jb SHORT ssvok sub al,06h ssvok: not al and al.lfh mov ah,04h call CODECWrite mov ah,05h call CODECWrite ret SetSynthvol ENDP setcovol PROC and al,7fh mov CDVol , al shr al,2 cmp al,05h jb SHORT scvok sub al,05h scvok: not al and al.lfh mov ah.Oθh call CODECWrite mov ah,07h call CODECWrite ret SetCDVol ENDP CODECWrite PROC mov dx, [esi .ssi_wiOAddressConcert] add dl,CONC_bCODECSTAT_OFF push eax mov ecx.lOOh call DisableBusMaster cwrl p : in al.dx test al.Olh loopnz cwrlp call EnableBusMaster pop eax add dl,CONC_wCODECCTL_OFF - CONC_bCODECSTAT OFF out dx.ax ret CODECWri te ENDP IFNDEF WIN_CODE iSRAssertwaveiRQ PROC bt DacFl ags , DAC_FLAG_INTENA jc SHORT lawcO ret i awcO : bts pi cFl ags , PIC_FLAG_VIRQ assert virtual IRQ jne SHORT l awcl if already asserted, skip ret i awcl: call Enablevi rtual PiC bts pi cFl ags , PIC_FLAG_VIRR assert virtual IRR cmp [esi . ssi_blRQ] , 0δh are we on the master PIC? jae SHORT i awslv if not, sk+p to slave sequence mov al , Mi cMask get master PIC mask state test al , waveiRQMask is our IRQ masked? jnz SHORT i awpnd if so, re-assert pend jmp SHORT i awdi s else, skip to dispatch iawslv: mov al ,SicMask get slave PIC mask state test al .waveiRQMask is our IRQ masked? jnz SHORT i awpnd if so, re-assert pend test MicMask,04h is the cascade IRQ masked? jnz SHORT i awpnd if so, re-assert pend jmp SHORT iawdis else, do it i awpnd : bts Pi CFl ags , PIC_FLAG_PEND ; fl ag i rq pendi ng ret iawdi s : bts pi cFl ags , PIC_FLAG_WAIT ; fl ag vi rtual servi ce sequence ret ISRAssertwaveiRQ ENDP ENDIF iRQService PROC test bl.δOh forced int? jz SHORT iswave IFNDEF WIN_CODE bts pi cFl ags , PIC_FLAG_WAIT ELSE bts Pi cFl ags , PIC_FLAG_VIRQ ; assert virtual IRQ mov eax, [esi .ssi_dwiRQHandle] mov ebx, [esi .ssi_dwCODECOwnerCur] vxDCal 1 vpiCD_Set_int_Rβquest ENDIF ret i swave : test bl,02h wave int? jz issynt if not, test synth int ; here we go - wave IRQ, case it out ., bt DacFlags ,DAC_FLAG_AUTO jc ISAUtO bt DmaFl ags,DMA_FLAG_AUTO jc issimAuto iSSingle: btr DacFl ags , DAC_FLAG_RUNNING btr SblFlags,SBL_FLAG_HISPEED mov dx, [esi .ssi_wlOAddressConcert] add dl ,CONC_bSERFMT_OFF or [esi . ssi_bSerFmt] , 40h mov al , [esi .ssi_bserFmt] out dx,al add dl,CONC_bDEVCTL_OFF - CONC_bSERFMT_OFF and [esi .ssi_boevCtl] .NOT 20h mov al , [esi .ssi_bDevCtl] out dx.al add dl ,CONC_bSERCTL_OFF CONC_bDEVCTL_OFF and [esi . ssi_bSerctl ] , NOT 02h mov al , [esi . ssi_bSerCtl] out dx . al or [esi .ssi_bserctl] ,02h mov al , [esi . ssi_bserctl ] out dx.al mov ax , DmaTermAddr mov DmaAddrCurr ,ax mov ax,DmaTermcount mov Dmacountcurr.ax cmp iperBuff, OOOOh jne SHORT issmOO mov al , [esi . ssi_DmaTCMask] or DmaStatus.al issmOO: jmp iSDisp iSSimAuto: mov ax , DACCount cmp ax,DmaCount jbe ISSingle cmp ExtraBytes.OOh je ISSingle ; siπr -auto, big DAC, unaligned ... mov dx, [esi .ssi_wiOAddressConcert] add dl ,CONC_bSERCTL_OFF sub IPerDispCurr.l . c SHORT issaOl jnz SHORT issaOO or [esi .ssi_bSerCtl] ,40h issaOO: and [esi.ssi_bSerCtl],NOT 02h mov al , [esi .ssi_bserctl] out dx.al or [esi .ssi_bserctl] ,02h mov al , [esi . ssi_bserctl] out dx.al ret issaOl: iSAuto: mov dx, [esi .ssi_wlOAddressconcert] add dl , CONC_bSERCTL_OFF and [esi.ssi_bSerCtl],NOT 02h mov al , [esi . ssi_bSerCtl ] out dx.al or [esi .ssi_bSerCtl] ,02h mov al , [esi . ssi_bSerCtl ] out dx.al cmp IperBuff.O je SHORT isamOl adjust buffer count sub iperBuffCurr.l SHORT isamOO jnz SHORT isam02 setup end skip add dl .CONCjlS IPCOFF - C CONC_bSERCTL_OFF mov al . EndSki p out dx .al jmp SHORT i sam02 isamOO: ; restore end skip mov ax . iperβuff mov iperBuffCurr .ax add dl , CONC_bSKIPC_OFF - C CONC_bSERCTL_OFF mov al . Normal Skip out dx .al isamOl: ; setup DMA TC status mov al , [esi . ssi_DmaτCMask] or D astatus .al isam02: ; evaluate int dispatch cmp iperDisp.O je ISDisp sub iperDispCurr.l jne isam03 mov ax.lperDisp mov IperDispCurr.ax jmp ISDisp i sam03 : ; we're outta here ... ret ISDisp: bt DacFlags ,DAC_FLAG_INTENA jne SHORT isnodis IFNDEF WIN_CODE call ISRAssertwaveiRQ ELSE bts Pi cFlags , PlC_FLAG_viRQ ; assert vi rtual IRQ mov eax, [esi . ssi.dwiRQHandle] mov ebx, [esi . ssi_dwC0DEC0wnercur] vxDCall VPlCD_Set_int_Request ENDIF isnodis: ret btr sbl Fl ags , SBL_FLAG_HISPEED bt DmaFl ags , DMA_FLAG_AUTO ; if DMA i n auto, skip SHORT ι sdmaa _« DmaFlags,DMA_FLAG_MASK ; auto-mask controller i sdmaa: mov DmaeountCurr ,O fffh expire DMA count mov al , [esi . ssi_DmaTCMask] set terminal condition or Dmastatus.al btr DacFlags ,DAC_FLAG_RUNNING mov dx, [esi .ssi_wiOAddressConcert] add dl ,CONC_bSERFMT_OFF or [esi .ssi_bSerFmt] ,40h no zeros ! ! ! mov al , [esi .ssi_bSerFmt] out dx.al add dl,CONC_bDEVCTL_OFF - CONC_bSERFMT_OFF and [esi.ssi_boevCtl],NOT 20h mov al , [esi .ssι_bDevCtl ] ; turn DAC off out dx.al mov dx, [esi .ssi_wiOAddressConcert] re-arm int add dl ,CONC_bSERCTL_OFF and [esi.ssi_bserctl],NOT 02h mov al , [esi .ssi_bserctl] out dx.al or [esi , ssi_bSerCtl ] ,02h mov al , [esi .ssi_bSerCtl] out dx.al i sfdi s : bt DacFl ags ,DAC_FLAG_INTENA jne SHORT issynt IFNDEF WIN_CODE call ISRAssertwaveiRQ ELSE bts Pi cFlags , PlC_FLAG_viRQ ; assert vi rtual IRQ mov eax , [esi . ssi_dwlRQHandl e] mov ebx , [esi . ssi_dwCODECOwnerCur] vxDCal 1 VPlCD_Set_lnt_Request ENDIF issynt: test bl ,04h got a synth int? jz SHORT i sexit if not, we're outta here mov dx, [esi .ssi_wlθAddressConcert] ; reset synth DAC count add dl ,CONC_bSERCTL_OFF and [esi . ssi_bSerCtl] ,NOT Olh ; re-arm int mov al , [esi .ssi_bSerCtl] out dx.al or [esi . ssij serctl] ,01h mov al , [esi ,ssi_bserctl] out dx.al add dl,CONC_bMEMPAGE_OFF CONC_bSERCTL_OFF mov al,CONC_SYNCTL_PAGE out dx.al add dl , CONC_wSYNCFC_OFF - CONC_bMEMPAGE_OFF call Di sableBusMaster in ax,dx call EnableBusMaster mov edx , [esi . ssi_l pDMABuf f erLi near3] cmp ax,256 jae SHORT isdoup add edx, 4*256 i sdoup : cmp ActiveCount,0 je SHORT i ski 11 dec ActiveCount push edx call _synth_update add esp, 4 i sexit: ret ski l l push Offffffffh call _synth_all_notes_off add esp,4 mov ActiveCount, 0 btr MpuFlags,MPU_FLAG_SYNTHON call SynthOff cal l _sy th_reinit_voi ces ret iRQService ENDP IFNDEF WIN_CODE vcpi Service: cmp ax.OdeOch ; handle goto v86 mode separately je VSGOV86 The following sequence handles all protected mode VCPI far-calls with the exception of the switch-to-vβ6-mode call. Here we have intercepted the far call and must translate it to the real EMS VCPI handler whose selectors were stored at init time. - Do NOT trash the eax or edx register entry values. - Restore ALL registers before returning. pushfd ; no ints ! cli push ecx ; save minimum regs mov ecx.es add ecx.Oβh push ds mov ds.ecx mov ssoffset.esp ; switch to local stack mov esp,OFFSET StackTop mov SSselector.ss mov ss.ecx push es ; setup global data selector add ecx.Oδh mov es.ecx push ebx ; save some working regs mov ecx.OOOδh ; find a GDT patch entry mov ebx.es cmp ebx,0020h jae SHORT csok mov ecx ,ebx add ecx.OOlδh csok mov VEselector.cx ; note GDT entry for call sgdt Cli entTab mov ebx.CTladdr ; point into client table add ebx, ecx mov ecx.es: [ebx+OOh] ; save current entries mov SaveGentri es+OOh , ecx mov ecx.es: [ebx+04h] mov SaveGentries+04h ,ecx mov ecx.es: [ebx+08h] mov SaveGentri es+Oβh , ecx mov ecx.es: [ebx+Och] mov SaveGentries+Och ,ecx mov ecx.es: [ebx+lOh] mov SaveGentries+lOh ,ecx mov ecx.es: [ebx+14h] mov SaveGentri es+14h , ecx mov ecx,VcpiGentri es+OOh ; patch w/ saved VCPI entries mov es: [ebx+OOh] , ecx mov ecx ,vcpiGentri es+04h mov es: [ebx+04h] ,ecx mov ecx, cpiGentries+Oδh mov es: [ebx+08h] , ecx mov ecx , cpiGentri es+Och mov es: [ebx+Och] ,ecx mov ecx , cpiGentries+lOh mov es: [ebx+10h] , ecx mov ecx , cpiGentri es+14h mov es: [ebx+14h] ,ecx call vcpi Entry ; make the real VCPI call mov ecx , SaveGentri es+OOh ; restore original client entries mov es: [ebx+OOh],ecx mov ecx, SaveGentri es+04h mov es:[ebx+04h] ,ecx mov ecx, SaveGentri es+08h mov es:[ebx+08h],ecx mov ecx,SaveGentries+Och mov es: [ebx+Och] ,ecx mov ecx , SaveGentri es+lOh mov es:[ebx+10h],ecx mov ecx , SaveGentries+14h mov es: [ebx+14h] ,ecx pop ebx restore working regs Pop es restore global selector lss esp.SpSave restore stack pop ds pop ecx popfd restore other stuff retf This is the intercept of the VCPI Client's protected-mode CUSE32) far call to the switch-to-v86-mode function. VSGOV86 : we can use eax here as long as we explicitly resore it to xxxxdeOch when we're done Cor just save/resore it). In any case, it's probably not save to grow the client stack for this call, i. e., don't push anything. we can completely trash first 8 bytes of the stack for temp storage since neither we nor the real VCPI Server will be performing a USE32 far return to the VCPI Client. All segment registers can be trashed except ds. mov [esp], ebx ; save some regs mov [esp+4] ,ds mov eax , cs ; setup seg registers add eax.Oδh mov ds , eax ; ds is for our data add eax,0δh mov es , eax ; es is for the full linear space Here we need to tell the VCPI mode switch call to return to our vδ6 code instead of the client's, save the client's vβ6 return address in the shared memory space so our vβ6 code can eventually return to it. mov eax, [esp+lOj ; save client vβ6 destination mov ax, [esp+8] mov ebx.psharedData mov es: [ebx+4] ,eax mov eax.vFoffset ; setup our vβ6 destination mov [esp+8] ,eax mov eax.VFsegment mov [esp+12] ,eax mov eax.es mov VEselector.ax ; note selector for far call sgdt ClientTab mov ebx.CTladdr ; point to client GDT patch entry add ebx, eax mov CTladdr.ebx save GDT entry laddr for later fixup mov eax,cr3 save client's cr3 mov Clientcr3,eax mov eax ,vcpiGentri es+OOh copy-in the real VCPI GDT entries mov es: [ebx+OOh] ,eax mov eax ,vcpiGentri es+04h mov es: [ebx+04h] ,eax mov eax ,vcpiGentri es+Oδh mov es: [ebx+08h] ,eax mov eax,vcpiGentr es+Och mov es ebx+Och] , eax mov eax ,vcpiGentri es+lOh mov es: [ebx+10h] ,eax mov eax ,vcpiGentri es+14h mov es: [ebx+14h] ,eax * mov ds, [esp+4] mov ebx, [esp] ; restore regs mov eax.OOOOdeOch jmp cs:vcpi Entry ; go to the real VCPI switch handler Debugservice: push ecx mov ecx.es add ecx.Oδh push ds mov ds.ecx mov ecx.drδ test ecx,08h jz dsexit push eax push edx push esi mov esi , gpssi and ecx, NOT Oδh mov drδ.ecx cmp KBLastByte.OeOh mov KBLastByte.al jne SHORT dskbex cmp al,30h je SHORT dsvup cmp al,2eh je SHORT dsvdn cmp al,20h i SHORT dsvmu jmp SHORT dskbex dsvup: mov al.Mastervol and al , 7fh cmp al, 127-4 ja SHORT dskdoO add al,4 call setMastervol jmp SHORT dskdoO dsvdn : mov al .Mastervol and al,7fh cmp al,4 jb SHORT dskdoO sub al,4 call SetMastervol jmp SHORT dskdoO dsvmu : mov al.Mastervol xor al.δOh cal l SetMastervol ; jmp SHORT dskdoO dskdoO : mov eax.ss l ar eax, eax test eax,400000h j SHORT dsspok a n nSd esp,Offffh dsspok : mov BYTE PTR [esp+δ],0 dskbex: pop esi pop edx pop eax pop ds pop ecx i retd dsexi t : mov ecx,cr3 and ecx,0fffff000h cmp ecx . EMMPageDi r Pop ds pop ecx - e SHORT dsisemm iretd dsi semm: jmp cs:DebugLink lnt67Service: Switch to protected mode call - make sure we're properly patched before linking to the VCPI server's handler. cmp ax.OdeOch switch call ? tch to proteted mode by the DOS extende ing us all of it's protected mode ccoonntteext, ow s.OOOlh ax si] use extender's page tables mov cx .es : [ebx] mov ebx . es : [ebx+2] mov es : [ebx+02h*8] , eax add eax. lδh mov es : [ebx+01h*δ] , eax mov es : [ebx+02h*8+4] , OOOOδeleh mov es : [ebx+01h*δ+4] , OOOOβeleh jmp ι ' 67pex This is a call from the vδ6 code to restore the VCPI client's GDT entries after a switch-to-vβ6-mode VCPI far call. i67tfix: cmp ax.Odeeon jne SHORT i67tgp push eax save regs push esi mov eax , cs add eax.Oδh mov ds ,eax add eax.Oδh mov es , eax mov eax,cr3 mov RegSave.eax mov eax,ClientCr3 ; need client's page context mov cr3,eax ; don't use stack uunntil restored * mov esi .CTladdr mov eax ,ConcGentri es+OOh restore client GDT entries mov es: [esi+OOh] ,eax mov eax , ConcGentri es+04h mov es:[esi+04h],eax mov eax , ConcGentri es+08h mov es:[esi+0δh] , eax mov eax, ConcGentri es+Och mov es:[esi+Och] ,eax mov eax,ConcGentri es+lOh mov es: [esi+10h] ,eax mov eax ,ConcGentri es+14h mov es:[esi+14h] ,eax mov eax.RegSave mov cr3,eax pop esi pop eax iretd This is us intercepting the VCPI v86 get-protected-mode-iπterface call , we will make the VCPI hanlder return to our vβ6 code for return value mods. Our v86 handler will then return control to the original v86 return destination. i67tgp: ; VCPI GetPMi call? validate stack pointer i67s32: mov ebx.pSharedData mov setup es:[ebx] for shared data es:[ebx+12],di mov save init di to shared space eax, [esp+6] mov save v86 return to shared space ax,[esp+4] mov es:[ebx+8] ,eax mov eax.VGoffset ; force v86 ret to vivo gpmi code mov [esp+4] , eax mov eax.VGsegment mov [esp+8],eax or PatchFlags.OOOlh pop eax jmp lnt67Link link to normal PM handler h proprietary command handler - no regs saved yet this hadler is invoked only from v86 mode with int 67, ax=deel, bh=cmd, bl=arg/return. push ecx push ds mov ecx , cs add ecx.Oδh mov ds.ecx mov ssoffset.esp mov SSselector.ss mov esp,OFFSET StackTop mov ss.ecx push es mov es , ecx push eax push edx push esi mov esi .gpSSl eld i67c00: cmp bh.OOh jne SHORT i67c01 push ebx call iRQService pop ebx jmp i67cex i67c01: cmp bh.Olh jne SHORT ι ' 67c04 call LoadShadowRegs mov SblTC.Offh mov AdFormat,08h mov eax.XMSPaddrLo add eax,OFFSET SynthBuffer mov [esi .ssi_lpDMABufferPhys3] ,eax mov ActiveCount,0 call _synth_init IFNDEF WIN. .CODE push ebx mov bl.Olh call Debugctl pop ebx ENDIF jmp i67cex i67c04: cmp bh,04h jne SHORT i67c05 and bl,7fh mov MT32Enable,bl push ebx call _syπth_set_MT32 add esp, 4 jmp i67cex ι ' 67c05 : cmp bh,05h jne SHORT i67c06 mov bl,MT32Enable jmp i67cex i67c06: cmp bh,06h jne SHORT i67c07 mov al.bl call SetSynthvol jmp i67cex i67c07: cmp bh,07h jne SHORT ι ' 67c08 mov bl.SynthVol jmp ι ' 67cex i67c08: cmp bh.Oδh jne SHORT l * 67c09 mov al.bl and al , 7fh call SetDACVol jmp i67cex i67c09: cmp bh,09h jne SHORT i67c0a mov bl.DACVol test bl.δOh jz SHORT i67nom xor bl.bl i67nom: jmp ι ' 67cex i67c0a: cmp bh , Oah jne SHORT i67c0b mov al.bl call SetCDVol jmp ι ' 67cex i67c0b: cmp bh.Obh jne SHORT i67c0c mov bl .CDVol jmp i67cex i67c0c: cmp bh.Och jne SHORT i67c0d not bl jmp i67cex i67c0d: cmp bh.Odh jne SHORT i67c0e call LoadShadowRegs mov SblTC.Offh mov AdFormat.Oδh mov eax , cs add eax,0010h mov es.eax mov esi , pSharedData mov eax.es: [esi] Get wave Phys and eax.OfffffOOOh Al i gn mov al,67h and add page tabl e attri bs mov edx.PTlGPtr mov ecx,3*0400h 67mwv: mov [edx] , eax add edx,4 add eax.lOOOh loop i67mwv mov eax , cr3 Fl ush TLB mov cr3 , eax mov eax.es: [esi] ; Get wave Phys and eax.OOOOOfffh ; extract page hangover add eax,40000000h ; Add to Linear address ClG) sub eax.XMSPaddrLo ; Derive logical pointer mov _waveData_ptr , eax ; and save jmp i67cex i67c0e: cmp bh.Oeh jne SHORT i67c0f mov Pi cFlags,OOOOh call DisableVirtualPic jmp i67cex i67c0f: cmp bh.Ofh jne SHORT i67cl0 call Debugctl jmp i 67cex i67cl0: cmp bh.lOh jne SHORT i67cll mov al.bl call SetMastervol jmp i67cex - i67cll: cmp bh,llh jne SHORT i67cex mov bl .Mastervol and bl , 7fh i67cex : pop esi pop edx pop eax pop es lss esp.SpSave pop ds pop ecx iretd Debugctl PROC or bl.bl jnz SHORT dcon mov eax,dr7 and eax,NOT 80h mov dr7,eax ret dcon: mov eax,cr4 or eax.Oδh mov cr4,eax mov eax,dr7 and eax, NOT OcOh mov dr7,-eax mov eax , 60h mov dr3,eax xor eax, eax mov dr6,eax mov eax,dr7 and eax, NOT OfOOOOOβOh or eax,200002δ0h mov dr7,eax mov KBLastByte.OOh ret Debugctl ENDP ; this funct loads the shadow registers from the IC LoadShadowRegs PROC ret LoadShadowRegs ENDP ; kill the virtual interrupt request KillVIRQ PROC btr Pi cFlags , PIC_FLAG_VIRQ jne SHORT kvqcl mov al,MicMask cmp [esi . ssi_bIRQ] ,08h jb SHORT kvqcO mov al.SicMask kvqcO : test al .waveiRQMask jz SHORT kvqcl bt PicFlags,PlC_FLAG_viSR jc SHORT kvqcl call DisablevirtualPlC kvqcl: btr Pi cFl ags , PIC_FLAG_viRR btr PicFlags,PIC_FLAG_PEND ret KillVIRQ ENDP ; enable PIC I/O trapping EπablevirtualPlC PROC ret EnablevirtualPIC ENDP IFNDEF WIN_CODE ; disable Pic I/O trapping DisablevirtualPlC PROC Di sabl evi rtual PlC ENDP ENDIF ; disable bus mastering in PCI config space DisableBusMaster PROC cmp Di rectAccess . O jne SHORT dbmok ret dbmok: push eax push edx mov dx.Ocfδh mov eax ,Di rectAccess out dx , eax add dl,4 mov ax,0101h out dx.ax pop edx pop eax ret DisableBusMaster ENDP ; enable bus mastering in PCI config space EnableBusMaster PROC cmp DirectAccess.O jne SHORT ebmok ret ebmok: push eax push edx mov dx.Ocfβh mov eax, Di rectAccess out dx , eax add dl,4 mov ax,0105h out dx.ax pop edx pop eax ret EnableBusMaster ENDP ENDIF IFNDEF WIN_CODE IFDEF DOS_DEBUG PUBLIC DbPUtβ DbPUtβ PROC push ebx push ecx push edx push ds push es mov ecx.cs add ecx.Oδh mov ds.ecx add ecx.Oδh mov es.ecx mov ebx.DebugPtr mov ecx, 2 dδl: rol al,4 mov edx, eax and edx, Ofh cmp edx.Oah jae SHORT d82 * add edx.'O' jmp SHORT d83 dδ2: add edx, 'A' - Oah d83: mov es: [ebx] ,dl add ebx, 2 cmp ebx,DEBUG_END jb dδnwl mov ebx , DEBUG.START dβnwl: loop SHORT d81 mov BYTE PTR es : [ebx] , ' ' add ebx, 2 cmp ebx,DEBUG_END jb d8nw2 mov ebx,DEBUG_START dδnw2 : mov BYTE PTR es : [ebx] , • * ' mov DebugPtr.ebx pop es pop ds pop edx pop ecx pop ebx ret DbPUtδ ENDP PUBLIC DbPutlδ DbPutlδ PROC push ebx push ecx push edx push ds push es mov ecx.cs add ecx.Oδh mov ds.ecx add ecx.Oδh mov es.ecx mov ebx.DebugPtr mov ecx,4 dl61: rol ax,4 mov edx, eax and edx, Ofh cmp edx,Oah jae dl62 add edx " , '0' jmp SHORT dl63 dl62: add edx, Α' - Oah dl63: mov es:[ebx],dl add ebx, 2 cmp ebx,DEBUG_END jb dlδnwl mov ebx,DEBUG_START dlδnwl: loop dl61 mov BYTE PTR es:[ebx] , ' ' add ebx,2 cmp ebx , DEBUG.END jb d!6nw2 mov ebx , DEBUG_START dl6nw2 : mov BYTE PTR es : [ebx] , ' * ' mov DebugPtr.ebx Pop es pop ds pop edx pop ecx pop ebx ret DbPUtlδ ENDP PUBLIC DbPut32 DbPUt32 PROC push ebx push ecx push edx push ds push es mov ecx.cs add ecx.Oδh. mov ds.ecx add ecx.Oδh mov es.ecx mov ebx.DebugPtr mov ecx, 8 dp321p: rol eax, 4 mov edx, eax and edx, Ofh cmp edx , Oah jae SHORT dp32hx add edx.'O' jmp SHORT dp32wr dp32hx: add edx.C'A' - Oah) dp32wr: mov es: [ebx] ,dl add ebx, 2 cmp ebx , DEBUG_END jb SHORT dp32na mov ebx ,DEBUG_START dp32na: loop dp321p mov BYTE PTR es : [ebx] , ' ' add ebx,2 cmp ebx,DEBUG_END jb dp32nb mov ebx , DEBUG_START dp32nb: mov BYTE PTR es : [ebx] , * * ' mov DebugPtr.ebx pop es pop ds pop edx pop ecx pop ebx ret DbPut32 ENDP ENDIF ELSE IFDEF DEBUG PUBLIC DbPut32 DbPut32 PROC push ecx mov ecx.ds mov ssoffset.esp mov SSselector.ss mov esp,OFFSET StackTop mov ss.ecx push es mov es.ecx Trace. .Out "IOHNDLRS: #EAX" pop es l ss esp . SpSave pop ecx ret DbPut32 ENDP ENDIF ENDIF IFDEF WIN_CODE VXD_LOCKED_CODE_ENDS VxD_LOCKED_DATA_S EG ENDIF ALIGN 4 IFNDEF WIN_CODE DevTabl e LABEL DWORD DD MpuTable, wssTable, SblTable, DD MicTable, MpuTabl e MdcTable, si cTabl e ELSE DevTabl e LABEL DWORD DD MpuTable, ssTable, SblTable, DD 0, MpuTable MdcTabl e ENDIF MpuTable LABEL DWORD DD MpuDataR, MpuDataw, MpuStatR, DD HOStStatR, MpuCmdW IONUII, IoNull, DD OdAddrR, ioNull OdAddrw, OdDataR, DD IONUII, OdDataw IONUII, IoNull, DD oplstatR, ioNull OplAddrw, IoNull, DD IONUII, OplDataw IONUII, IoNull. IoNull wssTable LABEL DWORD DD IONUII, IONUII, IoNull, DD jONull, IoNull IONUII , IoNull, DD AdAddrR, IONUII AdAddrw, AdDataR, DD AdStatusR, AdDataw Adstatusw, ioNull, IoNull SblTable LABEL DWORD DD OplstatR, OplAddrw, IoNull, DD oplstatR, OplDataw OplAddrw, IoNull, DD IONUII, OplDataw sblMixAddrw, DD IONUII, SblMixoataR, sblMixDataw sblResetw, ioNull, DD oplstatR, SblResetw OplAddrw, IONull, DD sblDataR, OplDataw IONUII, DD SblTxrR, SblDataR, ioNull SblCmdW, SblTxrR, DD sblRxrR, ioNull. Sblcmdw SblRxrR, ioNull IFNDEF WIN_CODE MicTable LABEL DWORD DD MicDataR, ENDIF MicCmdw, MicMaskR, MicMaskw MdcTable LABEL DWORD DD DmaOAddrR, DmaOAddrw, DmaOCountR, DmaOCountw DD DmalAddrR, DmalAddrw, DmalCountR, DmalCountw DD IONUII, ioNull, ioNull, IoNull DD Dma3AddrR, Dma3Addrw, Dma3CountR, Dma3Countw DD DmaStatusR, ioNull, IoNull , IoNull DD IONUII, DmaMaskw, IoNull, DmaModew DD IONUII, DmaFlopw, IoNull, IoNull DD IONUII, ioNull, IoNull , IoNull IFNDEF WIN_CODE SicTable LABEL DWORD DD SicDataR, SicCmdw, IONull, SicMaskw ENDIF pwaveβuffer DD ? pwaveData DD ? SbllOBuff DD 80δ0δ0δ0h DB 1024 DUPCOeeh) StackTop LABEL NEAR SynthBuffer DD 512 DUPCO) IFNDEF WIN_CODE IFDEF DOS_DEBUG Debugptr DD DEBUG_START ENDIF gpSSI DD OFFSET HwConfig ENDIF ClieπtCr3 DD ? Cl i entTab LABEL FWORD CTli it DW ? CTladdr DD ? EaxSave DD ? RegSave DD ? CR3Save DD ? SaveGentri es DD 6 DUPC?) Sbl Proc DD OFFSET SblParse MidiMessage DD OOOOOOffh ADFreqTable LABEL WORD DW 6000, 5513, 16000, 11025 DW 27429, lδ900, 32000, 22050 DW 22050, 37600, 37δOO, 44100 DW 4δ000, 33075, 9600, 6615 SpSave LABEL FWORD ssoffset DD ? ssselector DW 2 DUPC?) SblByteRate DW 22222 MpuFlags DW OOOOh sbl Flags DW OOOOh DacFlags DW OOOOh OR Cl SHL DAC_FLAG_INTENA) PicFLags DW OOOOh DmaFl ags DW OOOOh OR Cl SHL DMA_FLAG_MASK) PatchFlags DW δOOOh SbSrate DW 11025 DACCount DW ? DACCountsampl es DW ? DMACountSamples DW ? DMACountDWords DW ? DmaAddr DW ? DmaAddrCurr DW ? DmaTermAddr DW ? DmaCount DW ? OmaCountCurr DW ? DmaTermcount DW ? iperBuff DW ? iperβuffCurr DW ? iperDisp DW ? iperDispCurr DW ? StartSkip DB ? Normal Skip DB ? Endskip DB ? Extraβytes DB ? MidiDataC DB 2 Mi diCurre DB 1 MpuData DB Ofeh Mpustat DB 10111111b MpuTemp DB ? odAddr DB ? odi eCDCfg DB δOh AdAddr DB ? AdDACVolL DB ? AdDACVol R DB ? AdCDVOl L DB ? AdCDVolR DB ? AdFormat DB 08h Adconfig DB Och AdPinCtl DB 00h SblData DB Oaah SblRxr DB 01111111b SblTxr DB 01111111b sblTag DB ? SblToggle DB ? Dmastatus DB OOh Mi cCmd DB ? MicMask DB ? MicSkip DB OOh Si cCmd DB ? SicMask DB ? SicSkip DB OOh TagBvte DB 80h Toggle DB 0 SpkrStat DB OOh sblAccum DB Oaah sblxorPat DB 96h SblTC DB 0d3h SblMixAddr DB 40h OplAddr DB Olh oplstat DB 06h CdAddr DB 40h KBLastByte DB OOh MT32εnable DB OOh Mastervol DB ? Synthvol DB ? DACVol DB ? DACVolPhys DB ? CDVol DB ? SblMixer DB llh, llh, 99h , llh OOh - 07h DB llh, llh, llh Sblstereo Oδh - Odh DB llh Oeh - Ofh STE DB llh, llh, llh , llh lOh - 17h DB llh, llh, llh , llh lδh - Ifh DB llh, 99h, llh , 99h 20h - 27h DB llh, llh, llh, llh ; 28h - 2fh ActiveCount DW ? IFNDEF WIN_CODE _TEXT ENDS ELSE VXD_L0CKED_DATA_ENDS ENDIF END APPENDIX B 386p INCLUDE vivo. inc NUM-.CODEPAGES EQU 25 NMI--.INT EQU 02h TΣMER_INT EQU 08h DOS-SETVECT EQU 2Sh DOS-.TSR EQU 31h DOS-.GETVECT EQU 35h DOS_FREE EQU 49h OOS_MODIFY EQU 4ah PARMBLK STRUCT envseg WORD ? cmdoff WORD ? αndseg WORD ? fcblorfr oRD ? fcblseg WORD ? fcb2off WORD ? fcb2seg WORD ? PARMBLK ENDS .TEXT SEGMENT DWORD PUBLIC USElS 'COOE' ASSUME CS:.TEXT, ds:_TEXT ORG OlOOh Start: jmp Entry ; Resident Data Cnon-di scare ALIGN 2 FromScopβ LABEL NEAR vivoGentries DO 6 DUP(?) XfflSPaddr LABEL OWORO XPlower DW ? XPuppβr DW ? NumcodePages DW OOOOh HwConfig LABEL NEAR BasePort OW OOOOh wavePort DW ? OttoPort DW ? wavelrq Dβ-7 Midilrq OB ? DmaChan 08 ? ExtMidi DB ? SbEnablβ DB ? synthvol DB ? wavevol OB ? CdAuxVol DB ? BoardTypa DB ? ALIGN 2 Sharedoata LABEL NEAR lnt67Return LABEL DWORD l67Roffsβt DW ? l67Rsegment DW ? v86Rtturn LABEL DWORD v86Roffsβt DW ? v86Rsegment OW ? GetPmiDi DW ? DosLink LABE' "'WORD DLoffset .DW ? OLsegment DW ? MuxLink LABEL DWORD MLoffset DW ? MLsegment DW ? TimerLink LABEL DWORD TLoffset DW ? TLsegment OW ? Flags DW 0 FLAG.WIN EQU 0 MidlPic DW 0021h Midivβct DB 08h Midi unmask DB ? IFDEF DOS_DEBUG DβbugPtr DW 2*C80*0 + 0) ENDIF ; Resident Code (non-discardablt) starts htrt IFDEF DOS_DEBUG DbPutCh PROC push ts push di mov di.ObOOOh ; ap. mov es.di mov di , cs : DtbugPt r mov es:[di].al add cs:DβbugPtr,4 pop di pop es ret DbPutCh ENDP DbPutBytt PROC push ex push dx push di push ts mov di.es :DβbugPtr mov dx.ObOOOh mov es.dx mov ex, 2 c w fWfliiit • rol al,4 mov d .al and dl.Ofh cmp dl .Oah jb <M2 add dl.'A' - Oah jmp SHORT <M3 <9β2: add dl.'O' ( M3: mov es:[d1],d add di,2 cmp di, 2*80*24 jb nba xor di,d1 nba: l oop 3 ( 31 mov BYTE PTR es : [di] add di .2 cmp di.2*80*24 jb nbb xor di.di nbb: mov BYTE PTR es:[di], mov cs:oebugPtr,di pop es pop di pop dx pop ex rtt DbPutBytt ENDP DbPutword PROC push ex push dx push d1 push es mov d1,cs:otbugPtr mov dx.ObOOOh mov es.dx mov ex,4 ( 341: rol ax, mov dl.al and dl.Ofh cmp dl.Oah jb 902 add d .Α' - Oah jmp SHORT 093 <9β2: add dl.'O' 993: mov es:[di],dl add d1,2 cmp d1,2*80*24 jb nwa xor di,di nwa: oop 991 mov BYTE PTR ts:[d1],' ' add d1.2 cmp di,2*80*24 jb nwb xor di.di nwb: mov BYTE PTR ts : [d1 ] , ' * ' mov cs:0tbugptr,α1 -P es pop di pop dx pop ex ret DbPutword ENDP ENDIF Nmiisr PROC FAR push ax push dx mov dx , cr -sePort add ,dl ,N.. ΓAT.OFF in al ,dx in al.βlh and al.O h or al,08h out 61h.al and al,07h out δlh.al pop dx pop ax iret Nfflllsr ENDP Midiisr PROC FAR gs,FLAG W!N iret mi doit: push ax push bx mov ax , Odetlh mov bh.OOh int 67h pop bx pop ax mi exit: irtt Midi is r ENDP Dosisr PROC FAR bt cs: Flags, FLACWIN jne didoit mp cs:DosLink didoit: cmp ah,DOS_SETVECT je di svtct jmp cs:oosLink di svtct cmp al,02h je diskip cmp al.es: Midivβct . e diskip mp cs:DosLink diskip: iret oosisr ENDP MUXisr PROC FAR pushf cmp ax,1605h jβ wstrt cmp ax,1606h j« wstop milink: popf jmp cs:MuxLink wstrt: bts cs: Flags, FLAG_WIN push ax push dx mov dx.es:BastPort add dl,CONTROL_OFF xor al.al out dx.a 1 add dl.N.. ΓL_OFF-CONTROL_OFF out dx.al pop dx pop ax jmp mi link wstop: mov al.βch mov dx,cs:βastPort add dl,CONTROL_OFF mov al.Oβh out dx.al add dl ,NMICTL_OFF-CONTROL_OFF mov al.OaOh cmp cs:Sbεnab1β,00h je wnosb or a1,40h wnosb: out dx.al pop dx pop ax jmp milink Muxisr ENDP Timerisr PROC FAR bt cs: Flags, FLAG WII jπc tidoit mp cs: TiratrLink tidoit: push ax push dx mov dx, cs:MldlPlc in al, dx and al, cs: Midi unmask out dx.al out βlh.al xor al, ,al out 70h,al pop dx pop ax jmp cs: :TimtrLink Timtrlsr ENDP GetPtm : push eax push ex ] ,0fτfτf067h ts ntrits mov ebx,14h pop ex pop eax jmp cs: lnt67Rtturn Fixup: mov ax.OdttOh int 67h jmp cs :v86Rβturn Tsrεnd LABEL NEAR Init Data (discardable) starts htrt ALIGN xmsεntry LABEL DWORD XMS far-call entry point xεoffstt DW ? XEsegmtnt OW ? xmsHandlt DW ? our XMS chunk handle Toscope LABEL NEAR discardable data sent to SCOPE.EXE EmmCr3 DD ? EmrnGdt OF ? Emmldt DF ? DW OFFSET FromScopt OW OFFSET HwConfig DW OFFSET GttPnri DW OFFSET Fixup DW OFFSET shartdoata ; param struct for .EXE spawning Paramβlock PARMBLK < ?, OFFSET Param, ?, 5ch, ?, 6ch, ? > FileHandle DW 0 SsPath D8 65 DUPC?) vscapβ32 08 '\VSCAPE 2.BIN',0 ssconfig 08 '\SSCONFIG.EXE\0 Scope 08 'NSCOPE.EXE'.O D0S4GW 08 '\DOS4GW.EXE' ,0 Quiet DB " ^G-QUIET * ,0 param DB 1. OUP(' ') Tmpβuff DB 128 DUP(?) ; init Code (discardable) starts here IFDEF DOS.DEBUG PriπtChar PROC push ax push dx mov dl .al mov ah,02h int 21h pop dx pop ax ret PrintChar ENDP printword PROC push ax push ex push dx mov CX,4 991: rol ax, mov d .a and dl.Ofh cmp dl .Oah jb 992 add dl.'A' - ( Oah jmp SHORT 993 992: add dl.'O' 993: push ax mov al.dl call PriπtChar pop ax loop 991 rol ax, pop dx pop ex pop ax ret printword ENDP PrintRβt PROC push ax push dx mov ah,02h mov dl.Odh int 21h mov dl.Oah int 21h pop dx pop ax ret PrintRβt ENDP PπntStr PROC push ax push dx push di mov ah,02h 091: mov dl.es :[di] inc di or dl.dl jz 992 int 21h jmp SHORT 991 992: pop di pop dx pop ax ret PrintStr ENDP ENOIF PrintMsg PROC push bp mov . bp.sp push ax push dx push si pop si pop dx pop ax pop bp ret PrintMsg ENOP wordToStr PROC push ex push dx mov CX, 4 991: rol ax, 4 mov dl.al and dl.Ofh cmp dl.Oah jb 992 add dl.'A' - Oi jmp SHORT 993 992: add dl.'O* 993: mov es:[d1],dl inc di loop 991 mov BYTE • R es : [di ] ,0 i nc di pop dx pop ex ret wordToStr ENDP StrCpy nputs : ds :si - source string es:di - destination string Return : Nothing. All regs are left unaffected except . . . di - this will point to the byte immediately foilowing the last dtsti nation byte that was copied to. strcpy PROC push ax ; save regs push si iw-WAJ-k•. mov al ; copy the string mov inc !.f[S: es: [ι si -..ι inc di cmp al.O jπt 991 pop si restore regs and exit pop ax rtt strcpy ENDP Entry: ; string ops forward always eld ; print a hello dorks message call PrintMsg DB Odh.Oah,' SoundscaptV∑VO(TM) Initialization Driver, version 3.22.',0dh,0 DB ' Copyright(c) 1995-1996, ENSONIQ Corp., All Rights Reserved. ' ,0dh, Oah,0 ; if we need to, frtt all but our 64k so wt can spawn later mov ax.sp cmp ax.Offfth jne csizok mov bx.es mov es.bx mov bx.lOOOh mov ah,DOS-_MOOIFY int 21h csizok: : set if loader is cor if not, assume .com and resize mov ax.es: [OOlδh] mov es.ax mov bx.es: [0016h] cmp ax.bx jt psizok mov es.ax mov bx.lOOOh mov ah,DOS_MOOIFY int 21h psizok: ; get the int 67 vector, make sure wβ got a memory manager mov ax,(DOS_GETVECT SHL 8) OR 67h int 21h call Prinr i DB Odh.Oah.O/ . ' error: Memory Manager not detβu .id. ' .Odh.Oah DB ' Soundscapev∑vo requires EMM386 or similar. ' ,0dn. Oah.0 jmp exit emvok : see if we're already loaded mov ax.Odeelh mov bx,Ob55h int 67h cmp bl.Oaah jne ssnpres call PrintMsg DB Odh.Oah, ' Soundscapβvivo driver is currently loaded ..." .Odh.Oah, 0 jmp exit ssnpres: locate our path and save it xor di.di mov es,cs:[0O2ch] mov ex,Offffh xor al.al envlp: repne scasb cmp al.es: [d1l jne SHORT envlp mov bx.di add di,3 push ds SsPath SSPath mov BYTE PTR [d1],0 ; set the local quiet environment var for 4GW spawn mov di ,bx mov es.es: [002ch} mov Si,OFFSET QUlβt call Strcpy mov BYTE PTR ts:[di],0 ; build tht parameter line for config spawn mov di .OFFSET Param+2 mov ax.ds mov es, x mov ax,OFFSET HwConfig call wordToStr setup config offset arg mov BYTE PTR [di-1],' * mov ax.ds call wordToStr setup segment arg mov BYTE PTR [d1-l],0dh sub d1.OFFSET Param+2 add length mov ax.di mov Param,al ; bui d the filename for the confi code mov si .OFFSET SSPath mov di .OFFSET Tmpβuff mov dx.ds me mov bx,OFFSET Paramβlock setup param block mov ax,cs:[002ch] mov (PARMBLK PTR bxl).envseg,ax mov (PARMBLK PTR bx, ).cmdseg,ds mov (PARMBLK PTR [bx, ).fcblseg,ds mov (PARMBLK PTR [bx]).fcb2seg.ds mov dx,OFFSET Tmpβuff mov ax.ds mov es.ax mov ax,4b00h and spawn the .exβ int 21h J n - cfgran call PrintMsg DB 0dh.0ah,07h, τ error: could not execute SSCONF∑G.EXE. ' ,Odh,Oah, 0 jmp exit cfgran: cmp BasβPort.O jne cfgok jmp ex t r efrftgoA-* 1 *. ; see i f wβ have to bai 1 now ... mov di.0080h xor ex, ex mov cl,[d1] ; look for /N cmd line arg or cl.cl jz nokill dec ex inc di mov ax.ds mov es.ax mov al.V rtpnt scasb jnz nokill mov al,[d1] and al.Odfh cmp al,'N' jne nokill call PrintMsg DB Odh, Oah,' initialization Complete, DOS drivers OT loaded. ', Odh. Oah, Odh jmp exit nokill: ; setup some H -depeπdent vars mvrset: and cl,07h add Midivtct.cl mov al.l shl al.cl not al mov Midi unmask, l ; stt if wt havt an XMS drivar mov ax,4300h int 2fh cmp al , 80h je xmspr call PrintMsg DB 0dh,0ah,07h, error: XMS services were not detected. ',Odh,Oah,0 jmp exit xmspr: ; get the XMS control function entry point mov ax,4310h int 2fh mov XEof t . bx mov xεsev, .πt . es ; get enough XMS for our 32-bit code mov dx,C4 * NUM_C0DEPAGES) mov ah ,09h call xmsεntry or ax, x jnz xmsok call PrintMsg DB 0dh,0ah,07h, τ error: Could not allocate required Extended Memory. ', Odh, jmp exit xmsok: ; lock the XMS block Chandlβ in dx), save phys addr mov ah.Och call xmsεntry or ax, x jnz xmlok call PrintMsg DB 0dh,0ah,07h, τ error: Could not lock Extended Memory. ',Odh.Oah,0 jmp xfexit xmlok: mov xplowβr.bx mov xpupptr.dx ; make sure we're below 4M mov. ea ,xmsPaddr add eax,CCNUM_CODEPAGES SHL 12) 1) cmp eax,400000h jb xm4ok ca l PrintMsg DB 0dh,0ah,07h, τ error: Could not allocate Exttndtd Memory below 4 Mbyte be .Odh.Oah OB ' Try loading SSINIT.COM before SMARTDRV.EXE* ,Odh,Oah,0 fxexit: jmp xuβxit xm4ok: ; gtt tht EMM386 environment and data for scopt.txt mov eax,cr3 mov EmmCr3 , eax sgdt EmmGdt s dt Emmldt build tht parameter lint for scopt spawn mov di .OFFSET Param+2 mov ax.ds mov es.ax mov si .OFFSET SSPath setup fpath and fname for D0S4GW call strcpy dtc di mov si .OFFSET Scope call Strcpy mov BYTE PTR [dl-l],' ' mov BYTE PTR [di],0 mov ax,OFFSET Toscopt call wordToStr setup data-out offset arg mov BYTE PTR [dl-l].' ' mov ax.ds call wordToStr setup segment arg mov BYTE PTR [di-l],0dh sub di .OFFSET Param+2 add length mov ax.di mov paraa.al ; build I tht filename for th mov si .OFFSET sspath mov di .OFFSET Tmpβuff mov dx.ds mov es.dx first copy path call Strcpy dac d1 mov Si .OFFSET DOS4GW append filename call Strcpy mov bx.Oi .ET ParamBlock setup param b -ck mov dx,OFFSET Tmpβuff mov ax , ds mov es , ax mov ax , 4b00h and spawn- the . exe int 21h jne scpran call PrintMsg DB 0dh,0ah,07h, τ error: Could not execute SCOPE. EXE. ', Odh, Oah, 0 jmp fxexit scpran: cmp NumCodβPages.O jne scpok mp fxexit scpok: mov dx,OFFSET Nmiisr ; install NMI strvict mov ax,(DOS_SETVECT SHL 8) OR 02h int 21h mov dx, OFFSET Mldl lsr ; install MIDI IRQ strvict mov al .Midivtct int 21h mov ax, (DOS_GETVECT SHL 8) OR 21h int 21h ; savt DOS vector mov DLoffsβt,bx mov DLsegment.es mov dx,OFFSET oosisr ; install our DOS strvict mov ah,DOS_SETVECT int 21h mov ax.CDOS.GETVECT SHL 8) OR 2fh int 21h ; savt MUX vector mov MLOffsβt.bx mov MLsegment,βs mov dx,OFFSET Muxlsr ; install our UX strvict mov ah,DOS_SETVECT int 21h mov ax , (DOS_GETVECT SHL 8) OR TIMER_INT int 21h ; savt timtr vector mov TLoffstt.bx mov TLsegraent.es mov dx,OFFSET Timβrlsr ; install our timtr strvice mov ah,DOS_SETVECT int 21h ; call synth-init and enable otto ints mov ax.Odβtlh mov bh.Olh int 67h mov dx.βastPort add dl.CONTROU-.OFF mov al.08h out dx.al ; wait, then un- utt tht synth mov dx.BasβPort add dl,0DAOOR-_0FF mov cx.OOOfh umwtlpo: push ex mov ex.Offffh umwtlpi : in al.dx loop umwtlpl pop ex loop umwtlpo cmp BoardTypβ.OOh jne unmver mov dx.es :wavtPort vivo on Co mov es, cs:[002ch] free our local environment seg mov ah, OOS-.FREE int 2Uι 1 mov dx, COFFSET TsrEnd + OOOfh) terminate / stay resident shr dx, 4 mov ax, (DOS.TSR SHL 8) int 21h xuexit: ; unlock XMS memory block mov dx.xmsHandle mov ah.Odh call xmsεntry xfexit: ; free XMS memory block mov dx.xmsHandle mov ah,Oah call xmsεntry exit: ; get outta htrt ret _TEXT ENDS END Start APPENDIX C ♦include <stdio.h> ♦include <stdlib.h> ♦include <i86.h> ♦define NUM_CODEPAGES 25 /* number of 4k pages occupied by V32 code */ //#define DEBUG /* Some standard typedefs ... */ typedef unsigned char BYTE; typedef unsigned short WORD; typedef unsigned long DWORD; /* The following structure is the data passed in from SSINIT */ typedef struct { DWORD CR3; /* EMM' s CR3 */ WORD GdtLimit; /* EMM' s GDT l imit, l inear address */ DWORD GdtLaddr; WORD IdtLimit; /* EMM' s IDT l imit , l i near address */ DWORD IdtLaddr; WORD DoOffset; /* output data offset */ WORD HwOffset; /* HW config data offset */ WORD GetPmiOff; /* offset of v86 get PMI return handler */ WORD Fixupoff; /* offset of v86 switch return handler */ WORD SharedOff; /* offset to V86/V32 shared data area */ } DATA_IN; /* The following structure points to SSlNU's resident data area typedef struct DWORD vivoGentries [6] ; /* vivo GDT entries for VCPI get PMI */ DWORD xmsPaddr; /* phys addr of Extended Mem */ WORD NumCodePages; /* number of 4k v32 code pages */ } DATA_RES; /* The following structure is SSINΓΓ'S HW config data */ typedef struct { WORD BasePort; /* Gate Array base port WORD waveport; /* cooec base port */ WORD SynthPort; /* Otto base port */ BYTE waveirq; /* Cooec/SB IRQ */ .BYTE Midilrq; /* synth IRQ */ BYTE DmaChan; /* CoDec/SB DMA channel */ BYTE ExtMidi ; /* External MIDI enable V BYTE sbEnable; /* SB emulation enable */ BYTE synthvol ; /* Synth volume */ BYTE wavevol : /* wave volume */ BYTE cdAuxvol ; /* CD/Aux volume */ BYTE BoardType; /* Vivo rev number */ } HW_CONFIG; / /** TThhee ffoolllloowwiinngg structure is the config data at the top of the v32 code */ typedef struct { BYTE 3umpτable[0xlc; /* jump instr's for multiple entry pts */ DWORD vivoGentries [6, /* the vivo VCPI Get PMI GOT entries */ DWORD vcpiGentries [6. /* the real VCPI Get PMI GDT entries */ DWORD SharedDataP; /* linear addr of v86/v32 shared data */ DWORD Midivectoff; /* IDT MIDI vect offset from NMI */ DWORD GetPmiOff; /* QWORD far ptr to v86 Get PMI handler */ DWORD Getpmiseg; DWORD Fixupoff; /* QWORD far ptr to v86 switch fixup handler * DWORD FixupSeg; DWORD l67Linkθff; /* PM link to the real Int 67 handler */ WORD ∑67Linksel ; DWORD vcpiεntryoff; /* PM entry point to the real VCPI handler */ WORD vcpi EntrySel ; WORD Hardwareoptions ; /* bit field for board type */ WORD BasePort; /* duplication of hardware config for V32 */ WORD wavePort; WORD SynthPort; BYTE waveirq; BYTE Midilrq; BYTE DmaChan ; BYTE ExtMidi; BYTE Synthvo 1 BYTE Synthvo . } V32.CONFIG; /* The following structure is the DOS mem used in the VCPI Get PMI call */ typedef struct { DWORD PagβTable[0x0400] ; DWORD vcpi Gentries [6] ; BYTE oosstack[0x0400 - 4*6]; } DOS_MEM; DWORD DosMalloc(DWORD size); void DosFree(wθRD selector); DWORD GetGDT i DWORD Getvcpi(wθRD dmseg) ; DWORD GetCR3(); /* This function returns the 386 CR3 value */ ♦pragma aux GetCR3 - \ mov eax,cr3" \ value [eax]; voi d Fl ushTLB (voi d) ; /* This function flushes the page table TLB */ ♦pragma aux Fl ushTLB - \ "mov eax,cr3" \ "mov cr3,eax" \ modify [eax]; void Set∑Cvoid); /* This function disables interrupts */ ♦pragma aux Setl » \ *sti"; void ClrlCvoid); /* This function enables interrupts */ ♦pragma aux Clr∑ - \ Vii"; void mainCint argc, char *argv[]) { s, *pgdt, "pidt, *ppdir; ooffset, dsegment; , dmsel - 0; 4] ; ♦ifdef -3EBUG fd - f open " SCOPE. DBG", "w"); ♦endif /* make sure we got the right arg count, make pointers and midivect */ i argc < 3 ) return; dioffset » CwORD) strtoulCargv[ll , NULL, 16); dsegment ■ CwORD) strtou!Cargv[2j , NULL, 16); pdi - COATA.IN *) CC(DWORD) dsegment « 4) + dioffset); pdr - (DATA_RES *) CCCDWORD) dsegment « 4) + pdi ->Doθf f set) ; phw * CHW_CONFIG *) CCCDWORD) dsegment « 4) + pdi->Hwθffset); midivect - Cphw->Midi∑rq < 8 ? 0x08 : 0x68) + phw->Midi∑rq; ♦ifdef DEBUG pdi->GdtLimit) ; pdi->IdtLimit) ; /* setup the D0S4GW page table-zero-pointer */ pd4g0 ■ CDWORD *) CGetCR3() & OxfffffOOOUL) ; pd4g0 - CDWORD *) C*pd4g0 & OxfffffOOOUL) ; /* setup laddr 0x180000 for EMM page dir, 0x181000 for var page tab ptr */ *Cpd4gO + 0x0180) - Cpdi->CR3 & OxfffffOOOUL) I 0x67u; ppdir - CDWORD *) OxlδOOOOUL; pptab - CDWORD volatile *) OxlβlOOOUL; /* point pptab at EMM'S page table for the GOT, create a ptr */ *Cpd4gO + 0x0181) - *Cppdir + Cpd1->GdtLaddr » 22)); FlushTLBO; /* setup page table and pointer for EMM'S GDT */ *Cpd4gO + 0x0182) « *Cpptab + CCpd1->GdtLaddr » 12) & 0x000003ffUL)) I 0x67u; *Cpd4g0 + 0x0183) - *Cpd4g0 + 0x0182) + OxlOOOu; pgdt « CPWORD *) C0xl82000UL I (pdi ->GdtLaddr & OxOOOOOfffUL)) ; /* point pptab at EMM'S page table for the IDT, create a ptr */ *(pd4g0 + 0x0181) - *(ppdir + (pdi->IdtLaddr » 22)); FlushTLB(); /* setup page table and pointer for EMM'S ∑DT */ *Cpd4gO + 0x0184) - *Cpptab + CCpdi->IdtLaddr » 12) & 0x000003ffUL)) I 0x67u; *Cpd4gO + 0x0185) « *Cpd4g0 + 0x0184) + OxlOOOu; pidt - CDWORD *) C0xl84000UL I Cpdi ->ldtLaddr & OxOOOOOfffUL)); /* open the code file in current .exe's directory */ , fname) ; /* get the DOS memory for the GetPM∑ cal l , setup far ptr */ i fC tmp - DosMal loc(5*1024UL)) ) { printf("\007 error : could not alocate DOS memory. \n") ; goto exit ; dmsel - (WORD) (tmp » 16) ; dmseg * (WORD) tmp; * (DWORD *) &pdm - OUL ; * ((WORD *) &pdm + 2) - dmsel ; /* get the fi rst chunk of our fi le */ freidCTmpβuff , 1, 1024 , fp) ; /* setup v32 config data pointer */ pv32 - CV32.CONFIG *) ATmpβuff ; /* make the Get PMI cal l , copy VCPI data i nto V32 config */ pv32->vcpi εntryθff - Getvcpi Cdmseg) ; forC i ■ 0 ; i < 6 ; ++i ) pv32->vcpiGentri es [i ] - pdm->vcpiGentries [i] ; /* setup the v∑vo GDT entries in v32 and data-out mem */ pv32->vivoGentri es [0] - pdr->vivoGentri es[0] ■ pv32->vivoGentri es [2] - pdr->vivoGentri es [2] - Cpdr->xmsPaddr « 16) I Oxffffu; tmp - Cρdr->xmsPaddr & OxffOOOOOOUL) I CCpdr->xmsPaddr » 16) & OxOOOOOOffuL) I 0x004f9300UL; pv32->vi voGentri p " I pdr->v voGentri es [1 tmp 1X00000800UL. pv32->vi voGentri t J pdr->vi voGentri es [3, tmp; pv32->v voGentri es [4' pdr->vi voGentri es [4, OxOOOOffffUL; pv32->vivoGentri es [5 pdr->vi voGentri es [5 Ox0Ocf9300UL; /* fill out the rest of the v32 config memory */ pv32->SharedDataP « CCDWORD) dsegment « 4) + pdi->sharedθf ; pv32->Midivectθff * (DWORD) (midivect - 2) « 3; pv32->GetPmioff - (DWORD) pdi->GetPmiθff; pv32->Fixupθff ■ (DWORD) pdi->Fixupoff; pv32->GetPmiSeg » pv32->FixupSeg - (DWORD) dsegment; pv32->l67LinkOff - (*Cpidt + C0x67u « 1) + 1) 4 OxffffOOOOUL) C*Cpidt + C0x67u « D) 4 OxOOOOffffUL); pv32->∑67LinkSβl - CWORD) (*Cpidt + C0x67U « 1)) » 16); pv32->Hardwareθptions - 1 « phw->BoardType; pv32->βasePort ■ phw->βasePort; pv32->wavePort - phw->wavePort; pv32->SyπthPort ■ phw->SynthPort; pv32->wavelrq « phw->wavelrq; pv32->MidiIrq - phw->Midilrq; pv32->omaChan - phw->omaChan; pv32->ExtMidi - phw->ExtMidi & 0x7f; i C phw->Boardτype — 0x00 ) pv32->SynthvolL - pv32->SynthVolR ■ phw->Synthvol & 0x7f; else pv32->SynthvolL ■ pv32->SynthVolR - 0x7f; ♦ifdef DEBUG fprintfCfd, "\nv32 Data ...\n"); fprintfCfd, "v∑vo GDT 0 XOβlx X081x\n" , pv32->vi voGentri es ■ 01 , pv32->vivoGentr fprintfCfd, "V∑VO GOT 1 %081x X081x\n" , pv32->vi voGentri es ,2, , pv32->vivoGeπtr fprintfCfd, "VIVO GDT 2 %081x X081x\n" , pv32->vi voGentri es ,4, , pv32->vfv Gehtr fprintfCfd, "VCPI GDT 0 XOβlx X081x\n" , pv32->vcpi Gentri es ,0, , pv32->VcpiGentr fprintfCfd, "VCPI GDT 1 XOβlx X081x\n" , pv32->vcpi Gentries 2, , pv32->vcpiGentr fprintfCfd, "VCPI GDT 2 XOβlx X081x\n" , pv32->vcpi Gentries 4] , pv32->vcpiGentr fprintfCfd, "SharedDataP X081x\n" , pv32->SharedθataP) ; fprintfCfd, "MidivectOff X081x\n" . pv32->Midivect0ff) ; fprintfCfd, "v86GetPMl XOβlx X081x\n" , pv32->GβtPmiStg, pv32->GetPmiθff) ; fpri ntfCfd, "v86Fixup XOβlx X081x\n" , pv32->FixupSeg, pv32->Fixupθff) ; fprintfCfd, "ΣNT67 Link X04x X081x\n" , pv32->∑67LinkSel , pv32->∑67LinkOff) ; fprintfCfd, "VCPI εntry X04x X081x\n" , pv32->vcpiεntrγSel , pv32->vcpi Entryθff) fprintfCfd, "Hardware Opts X04x\n" , pv32->Hardwareθptions) ; fpri ntfCfd, "Ports X04x X04x X04x\n" , pv32->βasePort, pv32->wavePort , pv32->Sy fprintfCfd, "IRQS X02x X02x\n" , pv32->wavelrq , pv32->Midi lrq) ; fprintfCfd, "DMA X02x\n" , pv32->omaChan) ; ♦endif /* make entri es i n page table at 0x200000+ for our code , make poi nter */ for( i - 0 , tmp - (pdr->xmsPaddr & OxfffffOOOUL) I 0x67 ; i < NUM_CODEPAGES + 1; ++i , tmp +- OxlOOOu ) *(pd4g0 + 0x0200 + i) ■ tmp; pxms ■ (DWORD *) (0x200000UL I Cpdr->xmsPaddr & OxOOOOOfffUL)) ; /* copy fi rst file chunk up to εxtended Memory */ forC i ■ 0; i < 256 ; ++i ) *pxms++ - τmpβuff[i ] ; /* now copy the rest of the code file up */ whileC I feofCfp) ) { 1 ■ CfreadCTmpBuff , 1, 1024 , fp) + 3) » 2 ; rorC i ■ 0; i < j ; ++i ) k pxms++ • τmpβuff[i]; } /* The next section will fill-in unused entries in the D0S4GW */ /* page table at lin-addr 0x180000+ and setup some pointers to allow */ /* us to access the page table, GOT, and IDT of the EMM. */ /* fill in the EMM'S page-table-zero with our XMS entries */ *Cpd4g0 + 0x0181) « "ppdir; FlushTLBC); for i - CwORD) Cpdr->xmsPaddr » 12), tmp ■ pdr->xmsPaddr I 0x67u; i < CwORD) (pdr->xmsPaddr » 12) + 25; ++i , tmp +- OxlOOOu ) *(pptab + i) - tmp; /* fi nd a GOT sp. . to patch starti ng from top */ for( i - ((pdi ->GdtLimit + 1) » 2) - 2 ; i >■ 2 ; i -- 2 ) i f( K*(pgdt ♦ i + 1)) ) break; i f( i < 2 ) { pri ntf("\007 error: could not i nstal l servi ce vectors . \n") ; return ; U i; ♦ifdef DEBUG fprintfCfd, selector X04x\n", i « 2) ; ♦endif /* patch the GOT with our three servi ce entries */ •Cpgdt + i) ■ *(pgdt + i + 2) - pdr->vi voGentri es [0] ; *(pgdt + i + 1) - pdr->vi voGentri es [11 ; *(pgdt + 1 + 3) ■ pdr->vi voGentri es ,3, ; *(pgdt + i + 4) - pdr->vi voGentri es .4. ; *(pgdt + i + 5) - pd r->vi voGent ri es [5 j ; /* initialize some hardware ... */ inp(phw->βasePort + 0x08) ; // NMI cl r outp(phw->βasePort + 0x02, 0x03); // UART reset outp(phw->βasePort + 0x02, 0x00); outp(phw->BasePort + 0x06, 0x00); // no Otto ints yet outp(phw->βasePort + 0x08, Oxfe); outp(phw->βasβPort + 0x09, Oxaa); outp(phw->βasePort + OxOa, 0x08); outp(phw->BasePort + 0x04, 0x03); outp(phw->βasePort + 0x05, 0x80 j (phw->DmaChan « 4)); outp(phw->βasePort + 0x04, 0x04) ; outp(phw->βasePort + 0x05, Cphw->wavelrq ■» 9 ? 0x00 : phw->waveirq «■ 5 ? 0x04 : 0x08) I (phw->Midilrq — 9 ? 0x00 : phw->Midilrq ■» 7 ? 0x02 : 0x03)); outp(phw->BasePort + 0x04, 0x06); outp(phw->BasePort + 0x05, 0x00); outp(phw->wavePort, 0x0c) ; outpCphw->wavePort + 1, 0x50); outp(phw->wavePort , 0x49) ; outp phw->wavePort + 1, 0x04); outp(phw->wavePort, 0x48); outp(phw->wavePort + 1, 0x00); outpCphw->wavePort, OxOa); outpCphw->wavePort + 1, 0x80); outp(phw->wavePort, Oxlb); outp(phw->wavePort + 1, 0x08); outp(phw->wavePort , 0x10) ; outp(phw->wavePort + 1, 0x80); outp(phw->wavePort, Oxll); outpCphw->wavβPort + 1, OxcO); outp phw->wavePort, 0x00); outpCphw->wavePort + 1, 0x8c); outpCphw->wavePort, 0x01); outpCphw->wavePort + 1, 0x8c); outpCphw->wavePort, 0x06); outp(phw->wavePort + 1, (phw->wavevol A 0x7f) » 1); outp<phw->wavePort, 0x07) ; outp(phw->wavePort + 1, Cphw->waveVol A 0x7f) » 1); outp(phw->wavePort, 0x02); outpCphw->wavePort + 1, Cphw->CdAuxvol A 0x7f) » 2); outpCphw->wavePort, 0x03) ; outp phw->wavePort + 1, Cphw->CdAuxvol A 0x7f) » 2); /* now create the IDT entries for our NMI, MID∑, DOS and Int67 services */ Clri ; tmp - (DWORD) i « 18; • Cpidt + C0x02u « 1)) - tmp; *Cpidt + (0x02u « 1) + 1) - OxOOOOβeOOUL; *(pidt + ((WORD) midivect « 1)) - tmp | 0x0004U; *(pidt + ((WORD) midivect « 1) + 1) - OxOOOOβeOOUL; *(pidt + (0x67u « 1)) - tmp | OxOOOcu; 105 WORD " "ags; WORD -» » WORD ds; WORD fs; WORD gs; WORO ip; WORD cs; WORO sp; WORD ss; RmRegs { OUL. OxlOOOUL. OUL, OUL. OUL, OUL. OUL. OxOOOOdeOlUL. 0x3000, OU, OU, OU, OU, OU, OU, 0xl400u, Ou}; RmRegs.ds ■ dmseg; RmRegs.es - dmseg; RmRegs. ss - dmseg; segread(As); r.w.ax ■ 0x0300; r.w.bx - 0x0067; r W CX * 0* r.x.edi - CDWORD) ARmRegs; s.es - s.ds; int386xC0x31, &r. &r, 4s); return RmRegs. ebx; 104 *(pidt C0x67u !) ♦ !) * OxOOOOeeOOUL; setl(); ♦ifdef DEBUG fprintfCfd, "\nv86 Resident Data ...\n"); fpriπtf(fd, "VIVO GOT 0 XOβlx X081x\n", pdr->vivoGentnes[O s , pdr->vιvoGentπes L fprintf(fd, "VIVO GOT 1 XOβlx X081x\n", pdr->vivoGentries[2. , pdr->vιvoGeπtπes[ fprintfCfd, "v∑VO GDT 2 XOβlx X081x\n", pdr->vivoGentri es [4] , pdr->vivoGeπtπes[ fprintfCfd, "XMS Paddr X081x\n", pdr->xmsPaddr) ; fprintfCfd, "Num Code Pages X04x\n" J pdr->NumCodePages) ; ♦endif , L .. * fill -out the rest of the resident data struct */ pdr->NumCodePages - NUM.CODEPAGES; exit: if dmsel ) DosFree(dmsel); ifC fp ) fclose(fp); ♦ifdef DEBUG fclose(fd); ♦endif return; } /* This function invokes the DPM∑ ∑NT-31h call for allocating DOS memory. */ DWORD DOSMallOC(DWORD Size) { union REGS r; r.w.bx - (WORD) ((size + 15) » 4); r.w.ax - 0x0100; iπt386(0x31, Ar, Ar); if( r.w.cflag ) return OUL; return Cr.x.edx « 16) I r.w.ax; > /* This function invokes the DPMI iNT-31h call for freeing DOS memory. */ void DosFreeCwORD selector) { union REGS r; r.w.dx ■ selector; r.w.ax - 0x0101; int386(0x31, Ar, Ar); return; /* This function invokes the DOS-mode VCP∑ ∑NT-67h call for acquiring the */ /* protected-mode interface. */ DWORD GetvcpiCwoRD dmseg) { union REGS r; struct SREGS s; static struct { DWORD edi; DWORD esi ; DWORD ebp; OWORO zero; DWORD ebx; DWORD edx; DWORD ecx; DWORD eax;

Claims: What is claimed: 1. A method of providing device virtualization to an application running under a DOS extender within a protected-mode context created for said DOS extender within in a DOS-based operating system environment of a processor at run-time, said protected-mode context created for said DOS extender including an interrupt descriptor table for said DOS extender (DOS extender IDT) , comprising the steps of: storing device emulation code at a predetermined address in a memory accessible to said processor; detecting a request from said DOS extender to switch to protected-mode; upon detection of said request by said DOS extender, patching said DOS extender IDT, at run-time, to include a vector to said device emulation code for a predetermined interrupt; and when said processor detects said predetermined interrupt during execution of said application, said processor referencing said vector to said device emulation code patched into said DOS extender IDT. 2. The method of claim 1, wherein said predetermined address is in extended memory addressable by said processor, and said storing step comprises the step of storing said device emulation code at said predetermined address at boot-time. 3. The method of claim 1, wherein said DOS-based operating system environment includes a virtual control program interface (VCPI) through which said DOS extender acquires said protected-mode context, said detecting step comprising the steps of: intercepting communications between said DOS extender and said VCPI during said execution of said application; and detecting startup of said DOS extender when a communication from said DOS extender to said VCPI is said request to switch to protected-mode. 4. The method of claim 1, wherein said step of patching said DOS extender IDT includes the steps of: intercepting said request to said VCPI to switch to protected-mode, said request including a page directory of said DOS extender; switching a page directory of a protected-mode context of said processor to said page directory of said DOS extender; accessing said DOS extender IDT and searching for said vector to said device emulation code; if said DOS extender IDT does not include said vector to said device emulation code, searching a global descriptor table of said DOS extender (DOS extender GDT) to determine what selector value corresponds to said device emulation code and then entering said selector value for said device emulation code into said DOS extender IDT; switching said page directory back to said page directory of said protected mode context of said processor; and jumping to said VCPI to execute said request to switch to protected-mode. 5. The method of claim 1, wherein said predetermined interrupt is a non-maskable interrupt which is generated when said application attempts to address a predetermined address related to the device to be emulated. 6. A system which provides device virtualization to an application running under a DOS extender in a DOS- based operating system environment of a processor, said DOS extender executing within a protected-mode context created for said DOS extender within said DOS-based operating system environment of said processor at run-time, said protected- mode context created for said DOS extender including an interrupt descriptor table for said DOS extender (DOS extender IDT) , said system comprising: device emulation code stored at a predetermined address in a memory accessible to said processor; a detection program which detects a request from said DOS extender to switch to protected-mode; and a driver which operates in said protected-mode context of said DOS extender and which, upon detection by said detection program of said request from said DOS extender to switch to protected-mode, patches said DOS extender IDT, at run-time, to include a vector to said device emulation code for a predetermined interrupt, whereby, when said processor detects said predetermined interrupt during execution of said application, said processor references said vector to said device emulation code patched into said DOS extender IDT by said driver. 7. The system of claim 6, wherein said predetermined address is in extended memory addressable by said processor, and said device emulation code is stored at said predetermined address at boot-time. 8. The system of claim 6, wherein said DOS-based operating system environment includes a virtual control program interface (VCPI) through which said DOS extender acquires said protected-mode context, and said detection program is a terminate-and-stay-resident (TSR) program which intercepts communications between said DOS extender and said VCPI during said execution of said application, said detection program detecting startup of said DOS extender when a communication from said DOS extender to said VCPI is said request to switch to protected-mode. 9. The system of claim 8, wherein said driver installs its interrupt vectors into the DOS extender IDT upon detection of said request to switch to protected-mode from said DOS extender to said VCPI, said interrupt vectors including said vector to said device emulation code for said predetermined interrupt, thereby making said device emulation code available to said DOS extender and said application in said protected-mode context of said DOS extender. 10. The system of claim 8, wherein said DOS extender sends said request to switch to protected-mode to said VCPI at system startup and again during execution of said application running under said DOS extender, said request including at least the following parameters : physical address of a Page Directory and a linear address and size of the DOS extender IDT and a global descriptor table of said DOS extender. 11. The system of claim 6, wherein said predetermined interrupt is a non-maskable interrupt which is generated when said application attempts to address a predetermined address related to the device to be emulated.

Inventors: FRIEL JOSEPH T, WEIR ANDREW P, FRIEL, JOSEPH, T., WEIR, ANDREW, P.
Applicants: ENSONIC CORP, FRIEL JOSEPH T, WEIR ANDREW P, ENSONIQ CORPORATION, FRIEL, JOSEPH, T., WEIR, ANDREW, P.
Application year: 1998
Main classification: G06F12
IPC codes: G06F9