From 79b4ebcb955cb173a1391275ae00f8e62360b6d5 Mon Sep 17 00:00:00 2001 From: sparky4 Date: Wed, 15 Jul 2015 15:42:23 -0500 Subject: [PATCH] GPL library! for sound!! ^o^ 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 --- {src/lib => 16}/16_snd.c | 0 {src/lib => 16}/16_snd.h | 0 makefile | 31 +++-- sountest.exe | Bin 44308 -> 68097 bytes src/lib/doslib/8254.c | 107 +++++++++++++++ src/lib/doslib/8254.h | 146 ++++++++++++++++++++ src/lib/doslib/adlib.c | 289 +++++++++++++++++++++++++++++++++++++++ src/lib/doslib/adlib.h | 138 +++++++++++++++++++ src/lib/doslib/cpu.c | 157 +++++++++++++++++++++ src/lib/doslib/cpu.h | 233 +++++++++++++++++++++++++++++++ src/sountest.c | 110 ++++++++++++--- 11 files changed, 1184 insertions(+), 27 deletions(-) rename {src/lib => 16}/16_snd.c (100%) rename {src/lib => 16}/16_snd.h (100%) create mode 100644 src/lib/doslib/8254.c create mode 100644 src/lib/doslib/8254.h create mode 100644 src/lib/doslib/adlib.c create mode 100644 src/lib/doslib/adlib.h create mode 100644 src/lib/doslib/cpu.c create mode 100644 src/lib/doslib/cpu.h diff --git a/src/lib/16_snd.c b/16/16_snd.c similarity index 100% rename from src/lib/16_snd.c rename to 16/16_snd.c diff --git a/src/lib/16_snd.h b/16/16_snd.h similarity index 100% rename from src/lib/16_snd.h rename to 16/16_snd.h diff --git a/makefile b/makefile index 5b671927..eb1bedab 100644 --- 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 diff --git a/sountest.exe b/sountest.exe index dd72ba42f1317c5dbb2cbfdd5806fcd2abda28f5..74c09fffec4196d55cf981d3172fc2627b05f2f6 100644 GIT binary patch literal 68097 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∓s7ysM7iZo|z+_6~3yIj?5q9XdP0mD*knk;HqkO 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 MvT7qHt$(g1fCm5Nd9<2b_4f- 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 diff --git a/src/lib/doslib/8254.c b/src/lib/doslib/8254.c new file mode 100644 index 00000000..3d940b0b --- /dev/null +++ b/src/lib/doslib/8254.c @@ -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. + * + * + * 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 +#include /* this is where Open Watcom hides the outp() etc. functions */ +#include + +#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 index 00000000..568f642d --- /dev/null +++ b/src/lib/doslib/8254.h @@ -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. + * + * + * 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 + +/* 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 index 00000000..dcb80eb9 --- /dev/null +++ b/src/lib/doslib/adlib.c @@ -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. + * + * + * 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 +#include /* this is where Open Watcom hides the outp() etc. functions */ +#include +#include +#include +#include +#include +#include + +#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 index 00000000..d4d1641b --- /dev/null +++ b/src/lib/doslib/adlib.h @@ -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. + * + * + * 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 + +#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 index 00000000..9b7caf95 --- /dev/null +++ b/src/lib/doslib/cpu.c @@ -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. + * + * + * 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 +# include +#endif + +#include +#include /* this is where Open Watcom hides the outp() etc. functions */ +#include +#include +#include +#include +#include +#include + +#include "src/lib/doslib/cpu.h" +//#include + +/* 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 +#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 index 00000000..f84974bb --- /dev/null +++ b/src/lib/doslib/cpu.h @@ -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. + * + * + */ + +#ifndef __HW_CPU_CPU_H +#define __HW_CPU_CPU_H + +#include +#include + +#if !defined(FAR) +# if defined(TARGET_WINDOWS) +# include +# 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 */ diff --git a/src/sountest.c b/src/sountest.c index 76ffeaec..e8574425 100644 --- a/src/sountest.c +++ b/src/sountest.c @@ -23,37 +23,113 @@ #include #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[]) { - static FMInstrument testInst = -{ -0x00, 0x01, /* modulator frequency multiple... 0x20 */ -0x00, 0x00, /* modulator frequency level... 0x40 */ -0xF0, 0xF0, /* modulator attack/decay... 0x60 */ -0x73, 0x73, /* modulator sustain/release... 0x80 */ -0x03, 0x00, /* output waveform distortion 0xE0 */ -0x36, /* feedback algorithm and strength 0xC0 */ -}; + word i; +// static FMInstrument testInst = +//{ +//0x00, 0x01, /* modulator frequency multiple... 0x20 */ +//0x00, 0x00, /* modulator frequency level... 0x40 */ +//0xF0, 0xF0, /* modulator attack/decay... 0x60 */ +//0x73, 0x73, /* modulator sustain/release... 0x80 */ +//0x03, 0x00, /* output waveform distortion 0xE0 */ +//0x36, /* feedback algorithm and strength 0xC0 */ +//}; 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"); - FMKeyOn(0, 0x106, 4); + printf("e"); + adlib_fm[0].mod.key_on = 1; + //FMKeyOn(0, 0x106, 4); } else { - FMKeyOff(0); - } + adlib_fm[0].mod.key_on = 0; + //FMKeyOff(0); + } + adlib_update_groupA0(0,&adlib_fm[0]); } printf("!\n"); IN_Shutdown(); -} \ No newline at end of file +} -- 2.39.5