]> 4ch.mooo.com Git - 16.git/commitdiff
GPL library! for sound!! ^o^
authorsparky4 <sparky4@cock.li>
Wed, 15 Jul 2015 20:42:23 +0000 (15:42 -0500)
committersparky4 <sparky4@cock.li>
Wed, 15 Jul 2015 20:42:23 +0000 (15:42 -0500)
renamed:    src/lib/16_snd.c -> 16/16_snd.c
renamed:    src/lib/16_snd.h -> 16/16_snd.h
modified:   makefile
modified:   sountest.exe
new file:   src/lib/doslib/8254.c
new file:   src/lib/doslib/8254.h
new file:   src/lib/doslib/adlib.c
new file:   src/lib/doslib/adlib.h
new file:   src/lib/doslib/cpu.c
new file:   src/lib/doslib/cpu.h
modified:   src/sountest.c

16/16_snd.c [moved from src/lib/16_snd.c with 100% similarity]
16/16_snd.h [moved from src/lib/16_snd.h with 100% similarity]
makefile
sountest.exe
src/lib/doslib/8254.c [new file with mode: 0644]
src/lib/doslib/8254.h [new file with mode: 0644]
src/lib/doslib/adlib.c [new file with mode: 0644]
src/lib/doslib/adlib.h [new file with mode: 0644]
src/lib/doslib/cpu.c [new file with mode: 0644]
src/lib/doslib/cpu.h [new file with mode: 0644]
src/sountest.c

similarity index 100%
rename from src/lib/16_snd.c
rename to 16/16_snd.c
similarity index 100%
rename from src/lib/16_snd.h
rename to 16/16_snd.h
index 5b671927ee2acd4470dcc487ad9434ea0eb59084..eb1bedab1663e60ad4c14266698a0fe4e8468b88 100644 (file)
--- a/makefile
+++ b/makefile
@@ -20,9 +20,11 @@ SRC=src$(DIRSEP)
 SRCLIB=$(SRC)lib$(DIRSEP)
 JSMNLIB=$(SRCLIB)jsmn$(DIRSEP)
 EXMMLIB=$(SRCLIB)exmm$(DIRSEP)
+DOSLIB=$(SRCLIB)doslib$(DIRSEP)
 WCPULIB=$(SRCLIB)wcpu$(DIRSEP)
 
-16LIBOBJS = 16_in.$(OBJ) 16_mm.$(OBJ) wcpu.$(OBJ) 16_head.$(OBJ) scroll16.$(OBJ) 16_ca.$(OBJ) 16_snd.$(OBJ)
+16LIBOBJS = 16_in.$(OBJ) 16_mm.$(OBJ) wcpu.$(OBJ) 16_head.$(OBJ) scroll16.$(OBJ) 16_ca.$(OBJ) adlib.$(OBJ) 8254.$(OBJ) cpu.$(OBJ)
+#16_snd.$(OBJ)
 GFXLIBOBJS = modex16.$(OBJ) bitmap.$(OBJ) planar.$(OBJ) 16text.$(OBJ)
 
 all: 16.exe test.exe pcxtest.exe test2.exe palettec.exe maptest.exe fmemtest.exe fonttest.exe exmmtest.exe fonttes0.exe fontgfx.exe sountest.exe
@@ -48,16 +50,16 @@ fonttest.exe: fonttest.$(OBJ) 16.lib
        wcl $(FLAGS) fonttest.$(OBJ) 16.lib
 
 fonttes0.exe: fonttes0.$(OBJ) 16.lib
-        wcl $(FLAGS) fonttes0.$(OBJ) 16.lib
+       wcl $(FLAGS) fonttes0.$(OBJ) 16.lib
 
 fontgfx.exe: fontgfx.$(OBJ) 16.lib
-        wcl $(FLAGS) fontgfx.$(OBJ) 16.lib
+       wcl $(FLAGS) fontgfx.$(OBJ) 16.lib
 
 inputest.exe: inputest.$(OBJ) 16.lib
        wcl $(FLAGS) inputest.$(OBJ) 16.lib
 
 sountest.exe: sountest.$(OBJ) 16.lib
-        wcl $(FLAGS) sountest.$(OBJ) 16.lib
+       wcl $(FLAGS) sountest.$(OBJ) 16.lib
 
 pcxtest.exe: pcxtest.$(OBJ) gfx.lib
        wcl $(FLAGS) pcxtest.$(OBJ) gfx.lib
@@ -81,7 +83,7 @@ fmemtest.exe: fmemtest.$(OBJ) 16.lib
        wcl $(FLAGS) fmemtest.$(OBJ) 16.lib
 
 exmmtest.exe: exmmtest.$(OBJ) 16.lib
-        wcl $(FLAGS) exmmtest.$(OBJ) 16.lib
+       wcl $(FLAGS) exmmtest.$(OBJ) 16.lib
 
 #
 #executable's objects
@@ -120,16 +122,16 @@ fonttest.$(OBJ): $(SRC)fonttest.c
        wcl $(FLAGS) -c $(SRC)fonttest.c
 
 fonttes0.$(OBJ): $(SRC)fonttes0.c
-        wcl $(FLAGS) -c $(SRC)fonttes0.c
+       wcl $(FLAGS) -c $(SRC)fonttes0.c
 
 fontgfx.$(OBJ): $(SRC)fontgfx.c
-        wcl $(FLAGS) -c $(SRC)fontgfx.c
+       wcl $(FLAGS) -c $(SRC)fontgfx.c
 
 inputest.$(OBJ): $(SRC)inputest.c
        wcl $(FLAGS) -c $(SRC)inputest.c
 
 sountest.$(OBJ): $(SRC)sountest.c
-        wcl $(FLAGS) -c $(SRC)sountest.c
+       wcl $(FLAGS) -c $(SRC)sountest.c
 
 exmmtest.$(OBJ): $(SRC)exmmtest.c
        wcl $(FLAGS) -c $(SRC)exmmtest.c
@@ -179,8 +181,17 @@ mapread.$(OBJ): $(SRCLIB)mapread.h $(SRCLIB)mapread.c 16.lib
 16_ca.$(OBJ): $(SRCLIB)16_ca.h $(SRCLIB)16_ca.c
        wcl $(FLAGS) -c $(SRCLIB)16_ca.c
 
-16_snd.$(OBJ): $(SRCLIB)16_snd.h $(SRCLIB)16_snd.c
-        wcl $(FLAGS) -c $(SRCLIB)16_snd.c
+#16_snd.$(OBJ): $(SRCLIB)16_snd.h $(SRCLIB)16_snd.c
+#      wcl $(FLAGS) -c $(SRCLIB)16_snd.c
+
+adlib.$(OBJ): $(DOSLIB)adlib.h $(DOSLIB)adlib.c
+       wcl $(FLAGS) -c $(DOSLIB)adlib.c
+
+8254.$(OBJ): $(DOSLIB)8254.h $(DOSLIB)8254.c
+       wcl $(FLAGS) -c $(DOSLIB)8254.c
+
+cpu.$(OBJ): $(DOSLIB)cpu.h $(DOSLIB)cpu.c
+       wcl $(FLAGS) -c $(DOSLIB)cpu.c
 
 16_head.$(OBJ): $(SRCLIB)16_head.h $(SRCLIB)16_head.c
        wcl $(FLAGS) -c $(SRCLIB)16_head.c
index dd72ba42f1317c5dbb2cbfdd5806fcd2abda28f5..74c09fffec4196d55cf981d3172fc2627b05f2f6 100644 (file)
Binary files a/sountest.exe and b/sountest.exe differ
diff --git a/src/lib/doslib/8254.c b/src/lib/doslib/8254.c
new file mode 100644 (file)
index 0000000..3d940b0
--- /dev/null
@@ -0,0 +1,107 @@
+/* 8254.c
+ *
+ * 8254 programmable interrupt timer control library.
+ * (C) 2008-2012 Jonathan Campbell.
+ * Hackipedia DOS library.
+ *
+ * This code is licensed under the LGPL.
+ * <insert LGPL legal text here>
+ *
+ * Compiles for intended target environments:
+ *   - MS-DOS [pure DOS mode, or Windows or OS/2 DOS Box]
+ *
+ * The 8254 Programmable Interrupt Timer is used on the PC platform for
+ * several purposes. 3 Timers are provided, which are used as:
+ *
+ *  Timer 0 - System timer. This is tied to IRQ 0 and under normal operation
+ *            provides the usual IRQ 0 18.2 ticks per second. DOS programs that
+ *            need higher resolution reprogram this timer to get it.
+ *
+ *  Timer 1 - Misc, varies. On older PC hardware this was tied to DRAM refresh.
+ *            Modern hardware cycles it for whatever purpose. Don't assume it's function.
+ *
+ *  Timer 2 - PC Speaker. This timer is configured to run as a square wave and it's
+ *            output is gated through the 8042 before going directly to a speaker in the
+ *            PC's computer case. When used, this timer allows your program to generate
+ *            audible beeps.
+ *
+ * On modern hardware this chip is either integrated into the core motherboard chipset or
+ * emulated for backwards compatibility.
+ */
+
+#include <stdio.h>
+#include <conio.h> /* this is where Open Watcom hides the outp() etc. functions */
+#include <stdlib.h>
+
+#include "src/lib/doslib/8254.h"
+
+uint32_t t8254_counter[3] = {0x10000,0x10000,0x10000};
+int8_t probed_8254_result = -1;
+
+int probe_8254() {
+       if (probed_8254_result >= 0)
+               return (int)probed_8254_result;
+
+       /* NTS: Reading port 0x43 does nothing. Intel's datasheet even mentions this in one of the tables.
+        *      Actual hardware experience tells me some motherboards DO return something but the correct
+        *      response (as seen in emulators in DOSBox) is to ignore, returning 0xFF */
+       {
+               /* read timer 0 and see if it comes back non-0xFF */
+               /* NTS: We MUST use the read_8254 function to read it in order. The previous
+                *      version of this code read it byte-wise. For some reason it seems,
+                *      some emulators including DOSBox don't take that well and they fail
+                *      to reset the MSB/LSB flip-flop. When that happens our timer counter
+                *      readings come out byte-swapped and we cannot provide proper timing
+                *      and sleep routines. Symptoms: A DOS program using our timing code
+                *      would have proper timing only on every other run. */
+               unsigned int patience = 128,cc;
+               unsigned short c;
+               do {
+                       c = read_8254(0);
+                       if (c == 0xFFFF) c = read_8254(0);
+                       if (c != 0xFFFF) break;
+                       for (cc=0;cc != 0xFFFFU;) cc++; /* delay */
+               } while (patience-- > 0);
+
+               if (c == 0xFF)
+                       return (probed_8254_result=0);
+       }
+
+       return (probed_8254_result=1);
+}
+
+unsigned long t8254_us2ticks(unsigned long a) {
+       /* FIXME: can you write a version that doesn't require 64-bit integers? */
+       uint64_t b;
+       if (a == 0) return 0;
+       b = (uint64_t)a * (uint64_t)T8254_REF_CLOCK_HZ;
+       return (unsigned long)(b / 1000000ULL);
+}
+
+unsigned long t8254_us2ticksr(unsigned long a,unsigned long *rem) {
+       /* FIXME: can you write a version that doesn't require 64-bit integers? */
+       uint64_t b;
+       if (a == 0) return 0;
+       b = (uint64_t)a * (uint64_t)T8254_REF_CLOCK_HZ;
+       *rem = (unsigned long)(b % 1000000ULL);
+       return (unsigned long)(b / 1000000ULL);
+}
+
+void t8254_wait(unsigned long ticks) {
+       uint16_t dec;
+       t8254_time_t pr,cr;
+       if (ticks <= 1) return;
+       ticks--;
+       cr = read_8254(0);
+       do {
+               pr = cr;
+               cr = read_8254(0);
+               if (cr > pr)
+                       dec = (pr + (uint16_t)t8254_counter[0] - cr);
+               else
+                       dec = (pr - cr);
+
+               ticks -= dec;
+       } while ((signed long)ticks >= 0L);
+}
+
diff --git a/src/lib/doslib/8254.h b/src/lib/doslib/8254.h
new file mode 100644 (file)
index 0000000..568f642
--- /dev/null
@@ -0,0 +1,146 @@
+/* 8254.h
+ *
+ * 8254 programmable interrupt timer control library.
+ * (C) 2008-2012 Jonathan Campbell.
+ * Hackipedia DOS library.
+ *
+ * This code is licensed under the LGPL.
+ * <insert LGPL legal text here>
+ *
+ * Compiles for intended target environments:
+ *   - MS-DOS [pure DOS mode, or Windows or OS/2 DOS Box] */
+#ifndef __HW_8254_8254_H
+#define __HW_8254_8254_H
+
+#include "src/lib/doslib/cpu.h"
+#include <stdint.h>
+
+/* WARNING: When calling these functions it is recommended that you disable interrupts
+ *          during the programming procedure. In MS-DOS mode there is nothing to stop
+ *          the BIOS from trying to use the 8254 chip at the same time you are. It is
+ *          entirely possible that it might read values back during an interrupt. If
+ *          you do not watch for that, you will get erroneous results from these
+ *          routines. Use hw/cpu.h _cli() and _sti() functions.
+ *
+ *          In contrast, it is extremely rare for interrupt handlers to mess with the
+ *          PC speaker gate port (and even if they do, the worst that can happen is
+ *          our code overrides what the IRQ is trying to do) */
+
+#define PC_SPEAKER_GATE                                                0x61
+
+/* 1.19318MHz from which the counter values divide down from */
+#define T8254_REF_CLOCK_HZ                                     1193180
+
+#define T8254_IRQ                                              0
+
+#define T8254_PORT(x)                                          ((x) + 0x40)
+#define T8254_TIMER_PORT(x)                                    T8254_PORT(x)
+#define T8254_CONTROL_PORT                                     T8254_PORT(3)
+
+#define T8254_MODE_0_INT_ON_TERMINAL_COUNT                     0
+#define T8254_MODE_1_HARDWARE_RETRIGGERABLE_ONE_SHOT           1
+#define T8254_MODE_2_RATE_GENERATOR                            2
+#define T8254_MODE_3_SQUARE_WAVE_MODE                          3
+#define T8254_MODE_4_SOFTWARE_TRIGGERED_STROBE                 4
+#define T8254_MODE_5_HARDWARE_TRIGGERED_STROBE                 5
+
+#define T8254_READBACK_COUNT                                   0x20
+#define T8254_READBACK_STATUS                                  0x10
+#define T8254_READBACK_TIMER_2                                 0x08
+#define T8254_READBACK_TIMER_1                                 0x04
+#define T8254_READBACK_TIMER_0                                 0x02
+#define T8254_READBACK_ALL                                     0x3E
+
+/* this represents one counter value in the 8254/8253 chipset library, including the
+ * value the chip reloads on finishing countdown.
+ * NTS: In 8254 hardware, a value of 0 is treated as 65536 because the chip decrements
+ *      THEN checks against zero. This allows the rate to drop as low as
+ *      1193180 / 65536 = 18.20648Hz on PC hardware */
+typedef uint16_t t8254_time_t;
+
+/* the 8254 (NOT 8253!) has a command that allows us to read the status and counter
+ * value of one or more latches (though contemporary hardware fails to emulate multi-
+ * counter latching from one command!). The status allows us to know what the counter
+ * was programmed as, the output of the counter at the time, and whether a new counter
+ * value was being loaded. The t8254_readback_t structure is used to read this info */
+struct t8254_readback_entry_t {
+       unsigned char           status;
+       t8254_time_t            count;
+};
+
+struct t8254_readback_t {
+       struct t8254_readback_entry_t   timer[3];
+};
+
+extern uint32_t t8254_counter[3];
+extern int8_t probed_8254_result;
+
+int probe_8254();
+void readback_8254(unsigned char what,struct t8254_readback_t *t); /* WARNING: 8254 only, will not work on 8253 chips in original PC/XT hardware */
+unsigned long t8254_us2ticks(unsigned long a);
+unsigned long t8254_us2ticksr(unsigned long a,unsigned long *rem);
+void t8254_wait(unsigned long ticks);
+
+static inline t8254_time_t read_8254_ncli(unsigned char timer) {
+       t8254_time_t x;
+
+       if (timer > 2) return 0;
+       outp(T8254_CONTROL_PORT,(timer << 6) | (0 << 4) | 0);   /* latch counter N, counter latch read */
+       x  = (t8254_time_t)inp(T8254_TIMER_PORT(timer));
+       x |= (t8254_time_t)inp(T8254_TIMER_PORT(timer)) << 8U;
+       return x;
+}
+
+static inline t8254_time_t read_8254(unsigned char timer) {
+       unsigned int flags;
+       t8254_time_t x;
+
+       flags = get_cpu_flags();
+       x = read_8254_ncli(timer);
+       _sti_if_flags(flags);
+       return x;
+}
+
+/* NTS: At the hardware level, count == 0 is equivalent to programming 0x10000 into it.
+ *      t8254_time_t is a 16-bit integer, and we write 16 bits, so 0 and 0x10000 is
+ *      the same thing to us anyway */
+static inline void write_8254_ncli(unsigned char timer,t8254_time_t count,unsigned char mode) {
+       if (timer > 2) return;
+       outp(T8254_CONTROL_PORT,(timer << 6) | (3 << 4) | (mode << 1)); /* set new time */
+       outp(T8254_TIMER_PORT(timer),count);
+       outp(T8254_TIMER_PORT(timer),count >> 8);
+       /* for our own timing code, keep track of what that count was. we can't read it back from H/W anyway */
+       t8254_counter[timer] = (count == 0 ? 0x10000 : count);
+}
+
+static inline void write_8254(unsigned char timer,t8254_time_t count,unsigned char mode) {
+       unsigned int flags;
+
+       flags = get_cpu_flags();
+       write_8254_ncli(timer,count,mode);
+       _sti_if_flags(flags);
+}
+
+static inline unsigned char t8254_pc_speaker_read_gate() {
+       return inp(PC_SPEAKER_GATE) & 3;
+}
+
+static inline void t8254_pc_speaker_set_gate(unsigned char m) {
+       unsigned char x;
+
+       x = inp(PC_SPEAKER_GATE);
+       x = (x & ~0x3) | (m & 3);
+       outp(PC_SPEAKER_GATE,x);
+}
+
+static inline void write_8254_system_timer(t8254_time_t max) {
+       write_8254(0,max,T8254_MODE_2_RATE_GENERATOR);
+}
+
+static inline void write_8254_pc_speaker(t8254_time_t max) {
+       write_8254(2,max,T8254_MODE_3_SQUARE_WAVE_MODE);
+}
+
+#endif /* __HW_8254_8254_H */
+
diff --git a/src/lib/doslib/adlib.c b/src/lib/doslib/adlib.c
new file mode 100644 (file)
index 0000000..dcb80eb
--- /dev/null
@@ -0,0 +1,289 @@
+/* adlib.c
+ *
+ * Adlib OPL2/OPL3 FM synthesizer chipset controller library.
+ * (C) 2010-2012 Jonathan Campbell.
+ * Hackipedia DOS library.
+ *
+ * This code is licensed under the LGPL.
+ * <insert LGPL legal text here>
+ *
+ * Compiles for intended target environments:
+ *   - MS-DOS [pure DOS mode, or Windows or OS/2 DOS Box]
+ *
+ * On most Sound Blaster compatible cards all the way up to the late 1990s, a
+ * Yamaha OPL2 or OPL3 chipset exists (or may be emulated on PCI cards) that
+ * responds to ports 388h-389h. Through these I/O ports you control the FM
+ * synthesizer engine. On some cards, a second OPL2 may exist at 38A-38Bh,
+ * and on ISA PnP cards, the OPL3 may be located at 38C-38Dh if software
+ * configured. */
+/* TODO: ISA PnP complementary library */
+/* TODO: Modifications to the library to support OPL2/OPL3 chipsets at I/O ports
+ *       other than 388h */
+#include <stdio.h>
+#include <conio.h> /* this is where Open Watcom hides the outp() etc. functions */
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <malloc.h>
+#include <fcntl.h>
+#include <dos.h>
+
+#include "src/lib/doslib/adlib.h"
+
+unsigned short                 adlib_voice_to_op_opl2[9] = {0x00,0x01,0x02, 0x08,0x09,0x0A, 0x10,0x11,0x12};
+/* NTS: There is a HOWTO out there stating that the registers line up 0,1,2,6,7,8,... == WRONG! */
+unsigned short                 adlib_voice_to_op_opl3[18] = {0x00,0x01,0x02, 0x08,0x09,0x0A, 0x10,0x11,0x12, 0x100,0x101,0x102, 0x108,0x109,0x10A, 0x110,0x111,0x112};
+unsigned short*                        adlib_voice_to_op = adlib_voice_to_op_opl2;
+
+struct adlib_reg_bd            adlib_reg_bd;
+struct adlib_fm_channel                adlib_fm[ADLIB_FM_VOICES];
+int                            adlib_fm_voices = 0;
+unsigned char                  adlib_flags = 0;
+
+struct adlib_fm_channel adlib_fm_preset_violin_opl3 = {
+       .mod = {0,      1,      1,      1,      1,      1,      42,     6,      1,      1,      4,      0,
+               3,      456,    1,      1,      1,      1,      4,      0,      5},
+       .car = {0,      1,      1,      1,      1,      1,      63,     4,      1,      1,      4,      0,
+               3,      456,    1,      1,      1,      1,      0,      0,      2}
+};
+
+struct adlib_fm_channel adlib_fm_preset_violin_opl2 = {
+       .mod = {0,      1,      1,      1,      1,      1,      42,     6,      1,      1,      4,      0,
+               3,      456,    1,      1,      1,      1,      2,      0,      1},
+       .car = {0,      1,      1,      1,      1,      1,      63,     4,      1,      1,      4,      0,
+               3,      456,    1,      1,      1,      1,      0,      0,      2}
+};
+
+struct adlib_fm_channel adlib_fm_preset_piano = {
+       .mod = {0,      0,      1,      1,      1,      1,      42,     10,     4,      2,      3,      0,
+               4,      456,    1,      1,      1,      1,      4,      0,      0},
+       .car = {0,      0,      1,      1,      1,      1,      63,     10,     1,      8,      3,      0,
+               4,      456,    1,      1,      1,      1,      0,      0,      0}
+};
+
+struct adlib_fm_channel adlib_fm_preset_harpsichord = {
+       .mod = {0,      0,      1,      1,      1,      1,      42,     10,     3,      2,      3,      0,
+               4,      456,    1,      1,      1,      1,      2,      0,      3},
+       .car = {0,      0,      1,      1,      1,      1,      63,     10,     5,      3,      3,      0,
+               4,      456,    1,      1,      1,      1,      0,      0,      3}
+};
+
+/* NTS: adjust the modulator total level value to vary between muted (27) and open (47) with
+ *      further adjustment if you want to mimick the change in sound when you blow harder */
+struct adlib_fm_channel adlib_fm_preset_horn = {
+       .mod = {0,      0,      1,      0,      1,      0,      47,     6,      1,      1,      7,      0,
+               4,      514,    1,      1,      1,      1,      0,      0,      0},
+       .car = {0,      0,      1,      0,      1,      0,      47,     8,      2,      2,      7,      0,
+               4,      456,    1,      1,      1,      1,      0,      0,      0}
+};
+
+struct adlib_fm_channel adlib_fm_preset_deep_bass_drum = {
+       .mod = {0,      0,      0,      0,      1,      0,      13,     7,      1,      0,      1,      0,
+               2,      456,    1,      1,      1,      1,      7,      0,      1},
+       .car = {0,      0,      1,      1,      1,      1,      63,     15,     2,      6,      1,      0,
+               2,      456,    1,      1,      1,      1,      0,      0,      0}
+};
+
+/* NTS: You can simulate hitting software or harder by adjusting the modulator total volume
+ *      as well as raising or lowering the frequency */
+struct adlib_fm_channel adlib_fm_preset_small_drum = {
+       .mod = {0,      0,      0,      1,      1,      1,      54,     15,     10,     15,     15,     0,
+               3,      456,    1,      1,      1,      1,      1,      0,      0},
+       .car = {0,      0,      1,      1,      1,      1,      63,     15,     7,      15,     15,     0,
+               3,      456,    1,      1,      1,      1,      1,      0,      0}
+};
+
+unsigned char adlib_read(unsigned short i) {
+       unsigned char c;
+       outp(ADLIB_IO_INDEX+((i>>8)*2),(unsigned char)i);
+       adlib_wait();
+       c = inp(ADLIB_IO_DATA+((i>>8)*2));
+       adlib_wait();
+       return c;
+}
+
+void adlib_write(unsigned short i,unsigned char d) {
+       outp(ADLIB_IO_INDEX+((i>>8)*2),(unsigned char)i);
+       adlib_wait();
+       outp(ADLIB_IO_DATA+((i>>8)*2),d);
+       adlib_wait();
+}
+
+/* TODO: adlib_write_imm_1() and adlib_write_imm_2()
+ *       this would allow DOS programs to use this ADLIB library from within
+ *       an interrupt routine */
+
+int probe_adlib(unsigned char sec) {
+       unsigned char a,b,retry=3;
+       unsigned short bas = sec ? 0x100 : 0;
+
+       /* this code uses the 8254 for timing */
+       if (!probe_8254())
+               return 1;
+
+       do {
+               adlib_write(0x04+bas,0x60);                     /* reset both timers */
+               adlib_write(0x04+bas,0x80);                     /* enable interrupts */
+               a = adlib_status(sec);
+               adlib_write(0x02+bas,0xFF);                     /* timer 1 */
+               adlib_write(0x04+bas,0x21);                     /* start timer 1 */
+               t8254_wait(t8254_us2ticks(100));
+               b = adlib_status(sec);
+               adlib_write(0x04+bas,0x60);                     /* reset both timers */
+               adlib_write(0x04+bas,0x00);                     /* disable interrupts */
+
+               if ((a&0xE0) == 0x00 && (b&0xE0) == 0xC0)
+                       return 1;
+
+       } while (--retry != 0);
+
+       return 0;
+}
+
+int init_adlib() {
+       adlib_flags = 0;
+       if (!probe_adlib(0))
+               return 0;
+
+       adlib_write(0x01,0x20); /* enable waveform select */
+       adlib_voice_to_op = adlib_voice_to_op_opl2;
+       adlib_fm_voices = 9;
+
+       if (probe_adlib(1)) {
+               adlib_fm_voices = 18;
+               adlib_flags = ADLIB_FM_DUAL_OPL2;
+       }
+       else {
+               /* NTS: "unofficial" method of detecting OPL3 */
+               if ((adlib_status(0) & 0x06) == 0) {
+                       adlib_fm_voices = 18;
+                       adlib_flags = ADLIB_FM_OPL3;
+                       adlib_voice_to_op = adlib_voice_to_op_opl3;
+
+                       /* init like an OPL3 */
+                       adlib_write(0x105,0x01);                /* set OPL3 bit */
+                       probe_adlib(0);
+                       adlib_write(0x104,0x00);                /* disable any 4op connections */
+               }
+       }
+
+       return 1;
+}
+
+void shutdown_adlib_opl3() {
+       if (adlib_flags & ADLIB_FM_OPL3) {
+               adlib_write(0x105,0x00);                /* clear OPL3 bit */
+               probe_adlib(0);
+               adlib_fm_voices = 9;
+               adlib_voice_to_op = adlib_voice_to_op_opl2;
+               adlib_flags &= ~ADLIB_FM_OPL3;
+       }
+}
+
+void shutdown_adlib() {
+       shutdown_adlib_opl3();
+}
+
+void adlib_update_group20(unsigned int op,struct adlib_fm_operator *f) {
+       adlib_write(0x20+op,    (f->am << 7) |
+                               (f->vibrato << 6) |
+                               (f->sustain << 5) |
+                               (f->key_scaling_rate << 4) |
+                               (f->mod_multiple << 0));
+}
+
+void adlib_update_group40(unsigned int op,struct adlib_fm_operator *f) {
+       adlib_write(0x40+op,    (f->level_key_scale << 6) |
+                               ((f->total_level^63) << 0));
+}
+
+void adlib_update_group60(unsigned int op,struct adlib_fm_operator *f) {
+       adlib_write(0x60+op,    (f->attack_rate << 4) |
+                               (f->decay_rate << 0));
+}
+
+void adlib_update_group80(unsigned int op,struct adlib_fm_operator *f) {
+       adlib_write(0x80+op,    (f->sustain_level << 4) |
+                               (f->release_rate << 0));
+}
+
+void adlib_update_groupA0(unsigned int channel,struct adlib_fm_channel *ch) {
+       struct adlib_fm_operator *f = &ch->mod;
+       unsigned int x = (channel >= 9) ? 0x100 : 0;
+       adlib_write(0xA0+(channel%9)+x,  f->f_number);
+       adlib_write(0xB0+(channel%9)+x, (f->key_on << 5) |
+                                       (f->octave << 2) |
+                                       (f->f_number >> 8));
+}
+
+void adlib_update_groupC0(unsigned int channel,struct adlib_fm_channel *ch) {
+       struct adlib_fm_operator *f = &ch->mod;
+       unsigned int x = (channel >= 9) ? 0x100 : 0;
+       adlib_write(0xC0+(channel%9)+x, (f->feedback << 1) |
+                                       (f->connection << 0) |
+                                       (f->ch_d << 7) |
+                                       (f->ch_c << 6) |
+                                       (f->ch_b << 5) |
+                                       (f->ch_a << 4));
+}
+
+void adlib_update_groupE0(unsigned int op,struct adlib_fm_operator *f) {
+       adlib_write(0xE0+op,    (f->waveform << 0));
+}
+
+void adlib_update_operator(unsigned int op,struct adlib_fm_operator *f) {
+       adlib_update_group20(op,f);
+       adlib_update_group40(op,f);
+       adlib_update_group60(op,f);
+       adlib_update_group80(op,f);
+       adlib_update_groupE0(op,f);
+}
+
+void adlib_update_bd(struct adlib_reg_bd *b) {
+       adlib_write(0xBD,       (b->am_depth << 7) |
+                               (b->vibrato_depth << 6) |
+                               (b->rythm_enable << 5) |
+                               (b->bass_drum_on << 4) |
+                               (b->snare_drum_on << 3) |
+                               (b->tom_tom_on << 2) |
+                               (b->cymbal_on << 1) |
+                               (b->hi_hat_on << 0));
+}
+
+void adlib_apply_all() {
+       struct adlib_fm_operator *f;
+       unsigned short op;
+       int ch;
+
+       for (ch=0;ch < adlib_fm_voices;ch++) {
+               f = &adlib_fm[ch].mod; op = adlib_voice_to_op[ch];   adlib_update_operator(op,f);
+               f = &adlib_fm[ch].car; op = adlib_voice_to_op[ch]+3; adlib_update_operator(op,f);
+               adlib_update_groupA0(ch,&adlib_fm[ch]);
+               adlib_update_groupC0(ch,&adlib_fm[ch]);
+       }
+       adlib_update_bd(&adlib_reg_bd);
+}
+
+double adlib_fm_op_to_freq(struct adlib_fm_operator *f) {
+       unsigned long t = (unsigned long)f->f_number * 49716UL;
+       return (double)t / (1UL << (20UL - (unsigned long)f->octave));
+}
+
+void adlib_freq_to_fm_op(struct adlib_fm_operator *f,double freq) {
+       unsigned long l;
+
+       freq *= (1UL << (20UL - (unsigned long)f->octave));
+       l = (unsigned long)freq / 49716UL;
+       f->octave = 4;
+       while (l > 1023UL) {
+               f->octave++;
+               l >>= 1UL;
+       }
+       while (l != 0UL && l < 256UL) {
+               f->octave--;
+               l <<= 1UL;
+       }
+       f->f_number = l;
+}
+
diff --git a/src/lib/doslib/adlib.h b/src/lib/doslib/adlib.h
new file mode 100644 (file)
index 0000000..d4d1641
--- /dev/null
@@ -0,0 +1,138 @@
+/* adlib.h
+ *
+ * Adlib OPL2/OPL3 FM synthesizer chipset controller library.
+ * (C) 2010-2012 Jonathan Campbell.
+ * Hackipedia DOS library.
+ *
+ * This code is licensed under the LGPL.
+ * <insert LGPL legal text here>
+ *
+ * Compiles for intended target environments:
+ *   - MS-DOS [pure DOS mode, or Windows or OS/2 DOS Box] */
+#include "src/lib/doslib/cpu.h"
+#include "src/lib/doslib/8254.h"               /* 8254 timer */
+#include <stdint.h>
+
+#define ADLIB_FM_VOICES                        18
+
+#define ADLIB_IO_INDEX                 0x388
+#define ADLIB_IO_STATUS                        0x388
+#define ADLIB_IO_DATA                  0x389
+
+#define ADLIB_IO_INDEX2                        0x38A
+#define ADLIB_IO_STATUS2               0x38A
+#define ADLIB_IO_DATA2                 0x38B
+
+/* Adlib status */
+#define ADLIB_STATUS_TIMERS_EXPIRED    0x80
+#define ADLIB_STATUS_TIMER1_EXPIRED    0x40
+#define ADLIB_STATUS_TIMER2_EXPIRED    0x20
+
+enum {
+       ADLIB_FM_DUAL_OPL2=0x01,
+       ADLIB_FM_OPL3=0x02
+};
+
+struct adlib_fm_operator {
+       /* 0x20-0x3F */
+       uint8_t                 am:1;                   /* bit 7: Apply amplitude modulation */
+       uint8_t                 vibrato:1;              /* bit 6: Apply vibrato */
+       uint8_t                 sustain:1;              /* bit 5: maintain sustain level */
+       uint8_t                 key_scaling_rate:1;     /* bit 4: increase ADSR enevelope speed as pitch increases */
+       uint8_t                 mod_multiple:4;         /* bits 0-3: modulator multiple (1=voice frequency, 2=one octave above) */
+       /* 0x40-0x5F */
+       uint8_t                 level_key_scale:2;      /* bits 7-6: decrease volume as frequency rises (0=none 1=1.5dB/8ve 2=3dB/8ve 3=6dB/8ve) */
+       uint8_t                 total_level:6;          /* bits 5-0: total output level (for sanity's sake, we maintain here as 0=silent 0x3F=full even though hardware is opposite) */
+       /* 0x60-0x7F */
+       uint8_t                 attack_rate:4;          /* bits 7-4: attack rate */
+       uint8_t                 decay_rate:4;           /* bits 3-0: decay rate */
+       /* 0x80-0x9F */
+       uint8_t                 sustain_level:4;        /* bits 7-4: sustain level */
+       uint8_t                 release_rate:4;         /* bits 3-0: release rate */
+       /* 0xA0-0xBF */
+       uint16_t                key_on:1;               /* bit 5: voice the channel */
+       uint16_t                octave:3;               /* bits 4-2: octave */
+       uint16_t                f_number:10;            /* bits 1-0, then bits 7-0: F-number (frequency) */
+       /* 0xC0-0xCF */
+       uint8_t                 ch_a:1;                 /* bit 4: OPL3: Channel A output */
+       uint8_t                 ch_b:1;                 /* bit 5: OPL3: Channel B output */
+       uint8_t                 ch_c:1;                 /* bit 6: OPL3: Channel C output */
+       uint8_t                 ch_d:1;                 /* bit 7: OPL3: Channel D output */
+       uint8_t                 feedback:3;             /* bits 3-1: feedback strength */
+       uint8_t                 connection:1;           /* bit 0: connection (operator 1 and 2 independent if set) */
+       /* 0xE0-0xFF */
+       uint8_t                 waveform:3;             /* bits 1-0: which waveform to use */
+};
+
+struct adlib_fm_channel {
+       struct adlib_fm_operator        mod,car;
+};
+
+struct adlib_reg_bd {
+       uint8_t                 am_depth:1;
+       uint8_t                 vibrato_depth:1;
+       uint8_t                 rythm_enable:1;
+       uint8_t                 bass_drum_on:1;
+       uint8_t                 snare_drum_on:1;
+       uint8_t                 tom_tom_on:1;
+       uint8_t                 cymbal_on:1;
+       uint8_t                 hi_hat_on:1;
+};
+
+int init_adlib();
+void shutdown_adlib();
+void shutdown_adlib_opl3();
+int probe_adlib(unsigned char sec);
+unsigned char adlib_read(unsigned short i);
+void adlib_write(unsigned short i,unsigned char d);
+void adlib_update_group20(unsigned int op,struct adlib_fm_operator *f);
+void adlib_update_group40(unsigned int op,struct adlib_fm_operator *f);
+void adlib_update_group60(unsigned int op,struct adlib_fm_operator *f);
+void adlib_update_group80(unsigned int op,struct adlib_fm_operator *f);
+void adlib_update_groupA0(unsigned int channel,struct adlib_fm_channel *ch);
+void adlib_update_groupC0(unsigned int channel,struct adlib_fm_channel *ch);
+void adlib_update_groupE0(unsigned int op,struct adlib_fm_operator *f);
+void adlib_update_operator(unsigned int op,struct adlib_fm_operator *f);
+void adlib_freq_to_fm_op(struct adlib_fm_operator *f,double freq);
+double adlib_fm_op_to_freq(struct adlib_fm_operator *f);
+void adlib_update_bd(struct adlib_reg_bd *b);
+void adlib_apply_all();
+
+extern unsigned short                  adlib_voice_to_op_opl2[9];
+extern unsigned short                  adlib_voice_to_op_opl3[18];
+extern unsigned short*                 adlib_voice_to_op;
+
+extern struct adlib_reg_bd             adlib_reg_bd;
+extern struct adlib_fm_channel         adlib_fm[ADLIB_FM_VOICES];
+extern int                             adlib_fm_voices;
+extern unsigned char                   adlib_flags;
+
+extern struct adlib_fm_channel         adlib_fm_preset_deep_bass_drum;
+extern struct adlib_fm_channel         adlib_fm_preset_violin_opl3;
+extern struct adlib_fm_channel         adlib_fm_preset_violin_opl2;
+extern struct adlib_fm_channel         adlib_fm_preset_harpsichord;
+extern struct adlib_fm_channel         adlib_fm_preset_small_drum;
+extern struct adlib_fm_channel         adlib_fm_preset_piano;
+extern struct adlib_fm_channel         adlib_fm_preset_horn;
+
+/* NTS: I have a Creative CT1350B card where we really do have to wait at least
+ *      33us per I/O access, because the OPL2 chip on it really is that slow.
+ *      
+ *      Peior to this fix, the adlib code would often fail on a real CT1350B
+ *      (unless run just after the Sound Blaster test program) and even if it
+ *      did run, only about 1/3rd of the voices would work. Upping the delay
+ *      to 40us for OPL3 and 100us for OPL2 resolved these issues. */
+static inline void adlib_wait() {
+       t8254_wait(t8254_us2ticks((adlib_flags & ADLIB_FM_OPL3) ? 40 : 100));
+}
+
+static inline unsigned char adlib_status(unsigned char which) {
+       adlib_wait();
+       return inp(ADLIB_IO_STATUS+(which*2));
+}
+
+static inline unsigned char adlib_status_imm(unsigned char which) {
+       return inp(ADLIB_IO_STATUS+(which*2));
+}
+
diff --git a/src/lib/doslib/cpu.c b/src/lib/doslib/cpu.c
new file mode 100644 (file)
index 0000000..9b7caf9
--- /dev/null
@@ -0,0 +1,157 @@
+/* cpu.c
+ *
+ * Runtime CPU detection library.
+ * (C) 2009-2012 Jonathan Campbell.
+ * Hackipedia DOS library.
+ *
+ * This code is licensed under the LGPL.
+ * <insert LGPL legal text here>
+ *
+ * Compiles for intended target environments:
+ *   - MS-DOS
+ *   - Windows 3.0/3.1/95/98/ME
+ *   - Windows NT 3.1/3.51/4.0/2000/XP/Vista/7
+ *   - OS/2 16-bit
+ *   - OS/2 32-bit
+ *
+ * A common library to autodetect the CPU at runtime. If the program calling us
+ * is interested, we can also provide Pentium CPUID and extended CPUID information.
+ * Also includes code to autodetect at runtime 1) if SSE is present and 2) if SSE
+ * is enabled by the OS and 3) if we can enable SSE. */
+
+/* FIXME: The 16-bit real mode DOS builds of this program are unable to detect CPUID under OS/2 2.x and OS/2 Warp 3. Why? */
+
+#if defined(TARGET_WINDOWS) && TARGET_MSDOS == 16
+/* Win16: We're probably on a 386, but we could be on a 286 if Windows 3.1 is in standard mode.
+ *        If the user manages to run us under Windows 3.0, we could also run in 8086 real mode.
+ *        We still do the tests so the Windows API cannot deceive us, but we still need GetWinFlags
+ *        to tell between 8086 real mode + virtual8086 mode and protected mode. */
+# include <windows.h>
+# include <toolhelp.h>
+#endif
+
+#include <stdio.h>
+#include <conio.h> /* this is where Open Watcom hides the outp() etc. functions */
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <assert.h>
+#include <fcntl.h>
+#include <dos.h>
+
+#include "src/lib/doslib/cpu.h"
+//#include <hw/dos/dos.h>
+
+/* DEBUG: Flush out calls that aren't there */
+#ifdef TARGET_OS2
+# define int86 ___EVIL___
+# define ntvdm_RegisterModule ___EVIL___
+# define ntvdm_UnregisterModule ___EVIL___
+# define _dos_getvect ___EVIL___
+# define _dos_setvect ___EVIL___
+#endif
+
+#if defined(TARGET_WINDOWS) && TARGET_MSDOS == 16
+# include <hw/dos/winfcon.h>
+#endif
+
+char                           cpu_cpuid_vendor[13]={0};
+struct cpu_cpuid_features      cpu_cpuid_features = {0};
+signed char                    cpu_basic_level = -1;
+uint32_t                       cpu_cpuid_max = 0;
+unsigned char                  cpu_flags = 0;
+uint16_t                       cpu_tmp1 = 0;
+
+void cpu_probe() {
+#if TARGET_MSDOS == 32
+       /* we're obviously in 32-bit protected mode, or else this code would not be running at all */
+       /* Applies to: 32-bit DOS, Win32, Win95/98/ME/NT/2000/XP/etc. */
+       cpu_flags = CPU_FLAG_PROTECTED_MODE | CPU_FLAG_PROTECTED_MODE_32;
+#else
+       cpu_flags = 0;
+#endif
+
+       cpu_basic_level = cpu_basic_probe();
+
+#if defined(TARGET_OS2)
+       /* OS/2 wouldn't let a program like myself touch control registers. Are you crazy?!? */
+       cpu_flags |= CPU_FLAG_DONT_WRITE_RDTSC;
+#elif defined(TARGET_WINDOWS) && TARGET_MSDOS == 32
+       /* Under Windows 3.1 Win32s and Win 9x/ME/NT it's pretty much a given any attempt to work with
+        * control registers will fail. Win 9x/ME will silently ignore, and NT will fault it */
+       cpu_flags |= CPU_FLAG_DONT_WRITE_RDTSC;
+#elif !defined(TARGET_WINDOWS) && TARGET_MSDOS == 32
+       /* 32-bit DOS: Generally yes we can, but only if we're Ring 0 */
+       {
+               unsigned int s=0;
+
+               __asm {
+                       xor     eax,eax
+                       mov     ax,cs
+                       and     ax,3
+                       mov     s,eax
+               }
+
+               if (s != 0) cpu_flags |= CPU_FLAG_DONT_WRITE_RDTSC;
+       }
+#endif
+
+#if defined(TARGET_WINDOWS) && TARGET_MSDOS == 16
+       /* Windows 3.0/3.1 specific: are we in 32-bit protected mode? 16-bit protected mode? real mode?
+        * real mode with v86 mode (does Windows even work that way?). Note that GetWinFlags only appeared
+        * in Windows 3.0. If we're under Windows 2.x we have to use alternative detection methods, or
+        * else assume Real Mode since Windows 2.x usually does run that way. But: There are 286 and 386
+        * enhanced versions of Windows 2.x, so how do we detect those?
+        *
+        * NTS: This code doesn't even run under Windows 2.x. If we patch the binary to report itself as
+        *      a 2.x compatible and try to run it under Windows 2.11, the Windows kernel says it's
+        *      "out of memory" and then everything freezes (?!?). */
+       {
+# if TARGET_WINDOWS >= 30 /* If targeting Windows 3.0 or higher at compile time, then assume GetWinFlags() exists */
+               DWORD flags = GetWinFlags();
+               if (1) {
+# elif TARGET_WINDOWS >= 20 /* If targeting Windows 2.0 or higher, then check the system first in case we're run under 3.0 or higher */
+               /* FIXME: If locating the function by name fails, what ordinal do we search by? */
+               DWORD (PASCAL FAR *__GetWinFlags)() = (LPVOID)GetProcAddress(GetModuleHandle("KERNEL"),"GETWINFLAGS");
+               if (__GetWinFlags != NULL) {
+                       DWORD flags = __GetWinFlags();
+                       MessageBox(NULL,"Found it","",MB_OK);
+# else /* don't try. Windows 1.0 does not have GetWinFlags() and does not have any dynamic library functions either. There is
+          no GetModuleHandle, GetProcAddress, etc. */
+               if (0) {
+# endif
+                       if (WF_PMODE) {
+                               cpu_flags |= CPU_FLAG_DONT_WRITE_RDTSC;
+                               if (flags & WF_ENHANCED)
+                                       cpu_flags |= CPU_FLAG_PROTECTED_MODE | CPU_FLAG_PROTECTED_MODE_32;
+                               else if (flags & WF_STANDARD)
+                                       cpu_flags |= CPU_FLAG_PROTECTED_MODE;
+                       }
+                       /* I highly doubt Windows 3.0 "real mode" every involves virtual 8086 mode, but
+                        * just in case, check the machine status register. Since Windows 3.0 could run
+                        * on an 8086, we must be cautious to do this test only on a 286 or higher */
+                       else if (cpu_basic_level >= 2) {
+                               unsigned int tmp=0;
+
+                               __asm {
+                                       .286
+                                       smsw tmp
+                               }
+
+                               if (tmp & 1) {
+                                       /* if the PE bit is set, we're under Protected Mode
+                                        * that must mean that all of windows is in Real Mode, but overall
+                                        * the whole show is in virtual 8086 mode.
+                                        * We're assuming here that Windows would not lie to us about what mode is active.
+                                        *
+                                        * THEORY: Could this happen if Windows 3.0 were started in Real Mode
+                                        *         while EMM386.EXE is resident and active? */
+                                       cpu_flags |= CPU_FLAG_V86_ACTIVE;
+                                       cpu_flags |= CPU_FLAG_DONT_WRITE_RDTSC;
+                               }
+                       }
+               }
+       }
+#endif
+}
+
diff --git a/src/lib/doslib/cpu.h b/src/lib/doslib/cpu.h
new file mode 100644 (file)
index 0000000..f84974b
--- /dev/null
@@ -0,0 +1,233 @@
+/* cpu.h
+ *
+ * Runtime CPU detection library.
+ * (C) 2009-2012 Jonathan Campbell.
+ * Hackipedia DOS library.
+ *
+ * This code is licensed under the LGPL.
+ * <insert LGPL legal text here>
+ *
+ */
+
+#ifndef __HW_CPU_CPU_H
+#define __HW_CPU_CPU_H
+
+#include <stdint.h>
+#include <dos.h>
+
+#if !defined(FAR)
+# if defined(TARGET_WINDOWS)
+#  include <windows.h>
+# else
+#  if TARGET_MSDOS == 32
+#   define FAR
+#  else
+#   define FAR far
+#  endif
+# endif
+#endif
+
+/* FIX: Open Watcom does not provide inpd/outpd in 16-bit real mode, so we have to provide it ourself */
+/*      We assume for the same stupid reasons the pragma aux function can't be used because it's a 386 level instruction */
+#if TARGET_MSDOS == 16
+uint32_t __cdecl inpd(uint16_t port);
+void __cdecl outpd(uint16_t port,uint32_t data);
+#endif
+
+#if TARGET_MSDOS == 16
+static inline uint32_t ptr2phys(void far *p) {
+       return (((uint32_t)FP_SEG(p)) << 4UL) +
+               ((uint32_t)FP_OFF(p));
+}
+#endif
+
+#pragma pack(push,1)
+struct cpu_cpuid_features {
+       union {
+               uint32_t                raw[4]; /* EAX, EBX, EDX, ECX */
+               struct {
+                       uint32_t        todo[4];
+               } f;
+       } a;
+};
+
+struct cpu_cpuid_ext_features {
+       union {
+               uint32_t                raw[4]; /* EAX, EBX, ECX, EDX */
+               struct {
+                       uint32_t        todo[4];
+               } f;
+       } a;
+};
+
+struct cpu_cpuid_ext_cache_tlb {
+       union {
+               uint32_t                raw[4]; /* EAX, EBX, ECX, EDX */
+               struct {
+                       uint32_t        todo[4];
+               } f;
+       } a;
+};
+
+struct cpu_cpuid_ext_cache_tlb_l2 {
+       union {
+               uint32_t                raw[4]; /* EAX, EBX, ECX, EDX */
+               struct {
+                       uint32_t        todo[4];
+               } f;
+       } a;
+};
+
+struct cpu_cpuid_ext_longmode {
+       union {
+               uint32_t                raw[4]; /* EAX, EBX, ECX, EDX */
+               struct {
+                       uint32_t        todo[4];
+               } f;
+       } a;
+};
+
+struct cpu_cpuid_ext_apm {
+       union {
+               uint32_t                raw[1]; /* EAX */
+               struct {
+                       uint32_t        todo[1];
+               } f;
+       } a;
+};
+
+struct cpuid_result {
+       uint32_t                        eax,ebx,ecx,edx;
+};
+#pragma pack(pop)
+
+/* "Basic" CPU level */
+enum {
+       CPU_8086=0,             /* 0 */
+       CPU_186,
+       CPU_286,
+       CPU_386,
+       CPU_486,
+       CPU_586,
+       CPU_686,
+       CPU_MAX
+};
+
+extern const char *                    cpu_basic_level_str[CPU_MAX];
+extern char                            cpu_cpuid_vendor[13];
+extern struct cpu_cpuid_features       cpu_cpuid_features;
+extern signed char                     cpu_basic_level;
+extern uint32_t                                cpu_cpuid_max;
+extern unsigned char                   cpu_flags;
+extern uint16_t                                cpu_tmp1;
+
+/* compatability */
+#define cpu_v86_active (cpu_flags & CPU_FLAG_V86_ACTIVE)
+
+#define cpu_basic_level_to_string(x) (x >= 0 ? cpu_basic_level_str[x] : "?")
+
+/* CPU flag: CPU supports CPUID */
+#define CPU_FLAG_CPUID                 (1 << 0)
+#define CPU_FLAG_FPU                   (1 << 1)
+#define CPU_FLAG_CPUID_EXT             (1 << 2)
+#define CPU_FLAG_V86_ACTIVE            (1 << 3)
+#define CPU_FLAG_PROTECTED_MODE                (1 << 4)
+#define CPU_FLAG_PROTECTED_MODE_32     (1 << 5)
+/*      ^ Windows-specific: we are not only a 16-bit Win16 app, but Windows is running in 386 enhanced mode
+ *                          and we can safely use 32-bit registers and hacks. This will always be set for
+ *                          Win32 and 32-bit DOS, obviously. If set, PROTECTED_MODE is also set. */
+#define CPU_FLAG_DONT_WRITE_RDTSC      (1 << 6)
+
+void cpu_probe();
+int cpu_basic_probe(); /* external assembly language function */
+
+static void _cli();
+#pragma aux _cli = "cli"
+static void _sti();
+#pragma aux _sti = "sti"
+
+static inline void _sti_if_flags(unsigned int f) {
+       if (f&0x200) _sti(); /* if IF bit was set, then restore interrupts by STI */
+}
+
+/* NTS: remember for Watcom: 16-bit realmode sizeof(int) == 2, 32-bit flat mode sizeof(int) == 4 */
+static unsigned int get_cpu_flags();
+#if TARGET_MSDOS == 32
+#pragma aux get_cpu_flags = \
+       "pushfd"        \
+       "pop    eax"    \
+       value [eax];
+#else
+#pragma aux get_cpu_flags = \
+       "pushf"         \
+       "pop    ax"     \
+       value [ax];
+#endif
+
+static void set_cpu_flags(unsigned int f);
+#if TARGET_MSDOS == 32
+#pragma aux set_cpu_flags = \
+       "push   eax"    \
+       "popfd  "       \
+       parm [eax];
+#else
+#pragma aux set_cpu_flags = \
+       "push   ax"     \
+       "popf   "       \
+       parm [ax];
+#endif
+
+#if defined(TARGET_WINDOWS) && TARGET_MSDOS == 32
+/* Watcom does not offer int86/int386 for Win32s/Win9x/NT/etc */
+#else
+static inline void just_int86(unsigned char c,union REGS *r1,union REGS *r2) {
+# ifdef __386__
+       int386(c,r1,r2);
+# else
+       int86(c,r1,r2);
+# endif
+}
+#endif
+
+#if TARGET_MSDOS == 32
+static inline void cpu_cpuid(uint32_t idx,struct cpuid_result *x);
+#pragma aux cpu_cpuid = \
+       ".586p" \
+       "xor ebx,ebx" \
+       "mov ecx,ebx" \
+       "mov edx,ebx" \
+       "cpuid" \
+       "mov [esi],eax" \
+       "mov [esi+4],ebx" \
+       "mov [esi+8],ecx" \
+       "mov [esi+12],edx" \
+       parm [eax] [esi] \
+       modify [ebx ecx edx]
+#else
+void cpu_cpuid(uint32_t idx,struct cpuid_result *x);
+#endif
+
+#if TARGET_MSDOS == 32
+static inline uint64_t cpu_rdmsr(const uint32_t idx);
+#pragma aux cpu_rdmsr = \
+       ".586p" \
+       "rdmsr" \
+       parm [ecx] \
+       value [edx eax]
+
+static inline void cpu_wrmsr(const uint32_t idx,const uint64_t val);
+#pragma aux cpu_wrmsr = \
+       ".586p" \
+       "wrmsr" \
+       parm [ecx] [edx eax]
+#else
+/* This is too much code to inline insert everywhere---unless you want extra-large EXEs.
+ * It's better to conform to Watcom's register calling convention and make it a function.
+ * Note that if you look at the assembly language most of the code is shuffling the values
+ * around to convert EDX:EAX to AX:BX:CX:DX and disabling interrupts during the call. */
+/* see CPUASM.ASM */
+uint64_t cpu_rdmsr(const uint32_t idx);
+void cpu_wrmsr(const uint32_t idx,const uint64_t val);
+#endif
+
+#endif /* __HW_CPU_CPU_H */
index 76ffeaecceca6ab9a6695904d7fec359c9f3e66a..e85744255626ad27501bd21b3e218a2666ee44e1 100644 (file)
 #include <stdio.h>
 
 #include "src/lib/16_in.h"
-#include "src/lib/16_snd.h"
+//#include "src/lib/16_snd.h"
+#include "src/lib/doslib/adlib.h"
+#include "src/lib/doslib/8254.h"               /* 8254 timer */
+
+static unsigned int musical_scale[18] = {
+       0x1B0,                  /* E */
+       0x1CA,                  /* F */
+       0x1E5,                  /* f# */
+       0x202,                  /* G */
+       0x220,                  /* G# */
+       0x241,                  /* A */
+       0x263,                  /* A# */
+       0x287,                  /* B */
+       0x2AE,                  /* C */
+
+       0x2B0,                  /* E */
+       0x2CA,                  /* F */
+       0x2E5,                  /* f# */
+       0x302,                  /* G */
+       0x320,                  /* G# */
+       0x341,                  /* A */
+       0x363,                  /* A# */
+       0x387,                  /* B */
+       0x3AE,                  /* C */
+};
 
 void main(int argc, char near *argv[])\r
 {
-       static FMInstrument testInst =\r
-{\r
-0x00, 0x01,    /* modulator frequency multiple... 0x20 */\r
-0x00, 0x00,    /* modulator frequency level...    0x40 */\r
-0xF0, 0xF0,    /* modulator attack/decay...       0x60 */\r
-0x73, 0x73,    /* modulator sustain/release...    0x80 */\r
-0x03, 0x00,    /* output waveform distortion      0xE0 */\r
-0x36,                          /* feedback algorithm and strength 0xC0 */\r
-};
+       word i;
+//     static FMInstrument testInst =\r
+//{\r
+//0x00, 0x01,  /* modulator frequency multiple... 0x20 */\r
+//0x00, 0x00,  /* modulator frequency level...    0x40 */\r
+//0xF0, 0xF0,  /* modulator attack/decay...       0x60 */\r
+//0x73, 0x73,  /* modulator sustain/release...    0x80 */\r
+//0x03, 0x00,  /* output waveform distortion      0xE0 */\r
+//0x36,                                /* feedback algorithm and strength 0xC0 */\r
+//};
 
        IN_Startup();
-       FMReset();
-       FMSetVoice(0, &testInst);
+       //FMReset();
+       //FMSetVoice(0, &testInst);
+       if(!init_adlib())
+       {
+               printf("Cannot init library\n");
+               exit(-5);
+       }
+
+       if (adlib_fm_voices > 9)
+               printf("OPL3!\n");
+//             vga_bios_set_80x50_text();
+
+       memset(adlib_fm,0,sizeof(adlib_fm));
+       memset(&adlib_reg_bd,0,sizeof(adlib_reg_bd));
+       for (i=0;i < adlib_fm_voices;i++) {
+               struct adlib_fm_operator *f;
+               f = &adlib_fm[i].mod;
+               f->ch_a = f->ch_b = f->ch_c = f->ch_d = 1;
+               f = &adlib_fm[i].car;
+               f->ch_a = f->ch_b = f->ch_c = f->ch_d = 1;
+       }
+
+       for (i=0;i < /*adlib_fm_voices*/1;i++) {
+               struct adlib_fm_operator *f;
+
+               f = &adlib_fm[i].mod;
+               f->mod_multiple = 1;
+               f->total_level = 63 - 16;
+               f->attack_rate = 15;
+               f->decay_rate = 0;
+               f->sustain_level = 7;
+               f->release_rate = 7;
+               f->f_number = musical_scale[i%18];
+               f->octave = 4;
+               f->key_on = 0;
+
+               f = &adlib_fm[i].car;
+               f->mod_multiple = 1;
+               f->total_level = 63 - 16;
+               f->attack_rate = 15;
+               f->decay_rate = 0;
+               f->sustain_level = 7;
+               f->release_rate = 7;
+               f->f_number = 0;
+               f->octave = 0;
+               f->key_on = 0;
+       }
+
+       adlib_apply_all();
+
        printf("press Z!  to noise\npress ESC to quit");
        printf("p");
        while(!IN_qb(1))
        {
                if(IN_qb(44))
                {
-                       printf("e");\r
-                       FMKeyOn(0, 0x106, 4);
+                       printf("e");
+                       adlib_fm[0].mod.key_on = 1;\r
+                       //FMKeyOn(0, 0x106, 4);
                }
                else
                {
-                       FMKeyOff(0);
-               }\r
+                       adlib_fm[0].mod.key_on = 0;
+                       //FMKeyOff(0);
+               }
+               adlib_update_groupA0(0,&adlib_fm[0]);\r
        }
        printf("!\n");
        IN_Shutdown();
-}
\ No newline at end of file
+}