diff options
Diffstat (limited to 'src/fake86/blaster.c')
-rwxr-xr-x | src/fake86/blaster.c | 316 |
1 files changed, 316 insertions, 0 deletions
diff --git a/src/fake86/blaster.c b/src/fake86/blaster.c new file mode 100755 index 0000000..33c95c6 --- /dev/null +++ b/src/fake86/blaster.c @@ -0,0 +1,316 @@ +/* + Fake86: A portable, open-source 8086 PC emulator. + Copyright (C)2010-2013 Mike Chambers + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +/* blaster.c: functions to emulate a Creative Labs Sound Blaster Pro. */ + +#include "config.h" +#include <stdint.h> +#include <stdio.h> +#include <string.h> +#include "blaster.h" + +extern void set_port_write_redirector (uint16_t startport, uint16_t endport, void *callback); +extern void set_port_read_redirector (uint16_t startport, uint16_t endport, void *callback); +extern void doirq (uint8_t irqnum); +extern uint8_t read8237 (uint8_t channel); + +extern void outadlib (uint16_t portnum, uint8_t value); //on the Sound Blaster Pro, ports (base+0) and (base+1) are for +extern uint8_t inadlib (uint16_t portnum); //the OPL FM music chips, and are also mirrored at (base+8) (base+9) +//as well as 0x388 and 0x389 to remain compatible with the older adlib cards + +struct blaster_s blaster; + +void bufNewData (uint8_t value) { + if (blaster.memptr >= sizeof (blaster.mem) ) return; + blaster.mem[blaster.memptr] = value; + blaster.memptr++; +} + +extern uint64_t hostfreq; +void setsampleticks() { + if (blaster.samplerate == 0) { + blaster.sampleticks = 0; + return; + } + blaster.sampleticks = hostfreq / (uint64_t) blaster.samplerate; +} + +void cmdBlaster (uint8_t value) { + uint8_t recognized = 1; + if (blaster.waitforarg) { + switch (blaster.lastcmdval) { + case 0x10: //direct 8-bit sample output + blaster.sample = value; + break; + case 0x14: //8-bit single block DMA output + case 0x24: + case 0x91: + if (blaster.waitforarg == 2) { + blaster.blocksize = (blaster.blocksize & 0xFF00) | (uint32_t) value; + blaster.waitforarg = 3; + return; + } + else { + blaster.blocksize = (blaster.blocksize & 0x00FF) | ( (uint32_t) value << 8); +#ifdef DEBUG_BLASTER + printf ("[NOTICE] Sound Blaster DSP block transfer size set to %u\n", blaster.blocksize); +#endif + blaster.usingdma = 1; + blaster.blockstep = 0; + blaster.useautoinit = 0; + blaster.paused8 = 0; + blaster.speakerstate = 1; + } + break; + case 0x40: //set time constant + blaster.samplerate = (uint16_t) ( (uint32_t) 1000000 / (uint32_t) (256 - (uint32_t) value) ); + setsampleticks(); +#ifdef DEBUG_BLASTER + printf ("[DEBUG] Sound Blaster time constant received, sample rate = %u\n", blaster.samplerate); +#endif + break; + case 0x48: //set DSP block transfer size + if (blaster.waitforarg == 2) { + blaster.blocksize = (blaster.blocksize & 0xFF00) | (uint32_t) value; + blaster.waitforarg = 3; + return; + } + else { + blaster.blocksize = (blaster.blocksize & 0x00FF) | ( (uint32_t) value << 8); + //if (blaster.blocksize == 0) blaster.blocksize = 65536; + blaster.blockstep = 0; +#ifdef DEBUG_BLASTER + printf ("[NOTICE] Sound Blaster DSP block transfer size set to %u\n", blaster.blocksize); +#endif + } + break; + case 0xE0: //DSP identification for Sound Blaster 2.0 and newer (invert each bit and put in read buffer) + bufNewData (~value); + break; + case 0xE4: //DSP write test, put data value into read buffer + bufNewData (value); + blaster.lasttestval = value; + break; + default: + recognized = 0; + } + //blaster.waitforarg--; // = 0; + if (recognized) return; + } + + switch (value) { + case 0x10: + case 0x40: + case 0xE0: + case 0xE4: + blaster.waitforarg = 1; + break; + + case 0x14: //8-bit single block DMA output + case 0x24: + case 0x48: + case 0x91: + blaster.waitforarg = 2; + break; + + case 0x1C: //8-bit auto-init DMA output + case 0x2C: + blaster.usingdma = 1; + blaster.blockstep = 0; + blaster.useautoinit = 1; + blaster.paused8 = 0; + blaster.speakerstate = 1; + break; + + case 0xD0: //pause 8-bit DMA I/O + blaster.paused8 = 1; + case 0xD1: //speaker output on + blaster.speakerstate = 1; + break; + case 0xD3: //speaker output off + blaster.speakerstate = 0; + break; + case 0xD4: //continue 8-bit DMA I/O + blaster.paused8 = 0; + break; + case 0xD8: //get speaker status + if (blaster.speakerstate) bufNewData (0xFF); + else bufNewData (0x00); + break; + case 0xDA: //exit 8-bit auto-init DMA I/O mode + blaster.usingdma = 0; + break; + case 0xE1: //get DSP version info + blaster.memptr = 0; + bufNewData (blaster.dspmaj); + bufNewData (blaster.dspmin); + break; + case 0xE8: //DSP read test + blaster.memptr = 0; + bufNewData (blaster.lasttestval); + break; + case 0xF2: //force 8-bit IRQ + doirq (blaster.sbirq); + break; + case 0xF8: //undocumented command, clears in-buffer and inserts a null byte + blaster.memptr = 0; + bufNewData (0); + break; + default: + printf ("[NOTICE] Sound Blaster received unhandled command %02Xh\n", value); + break; + } +} + +uint8_t mixer[256], mixerindex = 0; +void outBlaster (uint16_t portnum, uint8_t value) { +#ifdef DEBUG_BLASTER + printf ("[DEBUG] outBlaster: port %Xh, value %02X\n", portnum, value); +#endif + portnum &= 0xF; + switch (portnum) { + case 0x0: + case 0x8: + outadlib (0x388, value); + break; + case 0x1: + case 0x9: + outadlib (0x389, value); + break; + case 0x4: //mixer address port + mixerindex = value; + break; + case 0x5: //mixer data + mixer[mixerindex] = value; + break; + case 0x6: //reset port + if ( (value == 0x00) && (blaster.lastresetval == 0x01) ) { + blaster.speakerstate = 0; + blaster.sample = 128; + blaster.waitforarg = 0; + blaster.memptr = 0; + blaster.usingdma = 0; + blaster.blocksize = 65535; + blaster.blockstep = 0; + bufNewData (0xAA); + memset (mixer, 0xEE, sizeof (mixer) ); +#ifdef DEBUG_BLASTER + printf ("[DEBUG] Sound Blaster received reset!\n"); +#endif + } + blaster.lastresetval = value; + break; + case 0xC: //write command/data + cmdBlaster (value); + if (blaster.waitforarg != 3) blaster.lastcmdval = value; + break; + } +} + +uint8_t inBlaster (uint16_t portnum) { + uint8_t ret = 0; +#ifdef DEBUG_BLASTER + static uint16_t lastread = 0; +#endif +#ifdef DEBUG_BLASTER + //if (lastread != portnum) printf ("[DEBUG] inBlaster: port %Xh, value ", portnum); +#endif + portnum &= 0xF; + switch (portnum) { + case 0x0: + case 0x8: + ret = inadlib (0x388); + break; + case 0x1: + case 0x9: + ret = inadlib (0x389); + break; + case 0x5: //mixer data + ret = mixer[mixerindex]; + break; + case 0xA: //read data + if (blaster.memptr == 0) { + ret = 0; + } + else { + ret = blaster.mem[0]; + memmove (&blaster.mem[0], &blaster.mem[1], sizeof (blaster.mem) - 1); + blaster.memptr--; + } + break; + case 0xE: //read-buffer status + if (blaster.memptr > 0) ret = 0x80; + else ret = 0x00; + break; + default: + ret = 0x00; + } +#ifdef DEBUG_BLASTER + //if (lastread != portnum) printf ("%02X\n", ret); + //lastread = portnum; +#endif + return (ret); +} + +//FILE *sbout = NULL; +void tickBlaster() { + if (!blaster.usingdma) return; + /*if (blaster.paused8) { + blaster.sample = 128; + return; + }*/ + //printf("tickBlaster();\n"); + blaster.sample = read8237 (blaster.sbdma); + //if (sbout != NULL) fwrite(&blaster.sample, 1, 1, sbout); + blaster.blockstep++; + if (blaster.blockstep > blaster.blocksize) { + doirq (blaster.sbirq); +#ifdef DEBUG_BLASTER + printf ("[NOTICE] Sound Blaster did IRQ\n"); +#endif + if (blaster.useautoinit) { + blaster.blockstep = 0; + } + else { + blaster.usingdma = 0; + } + } +} + +int16_t getBlasterSample() { + if (blaster.speakerstate == 0) return (0); + else return ( (int16_t) blaster.sample - 128); +} + +void mixerReset() { + memset (blaster.mixer.reg, 0, sizeof (blaster.mixer.reg) ); + blaster.mixer.reg[0x22] = blaster.mixer.reg[0x26] = blaster.mixer.reg[0x04] = (4 << 5) | (4 << 1); +} + +void initBlaster (uint16_t baseport, uint8_t irq) { + //sbout = fopen("sbout.raw", "wb"); + memset (&blaster, 0, sizeof (blaster) ); + blaster.dspmaj = 2; //emulate a Sound Blaster 2.0 + blaster.dspmin = 0; + blaster.sbirq = irq; + blaster.sbdma = 1; + mixerReset(); + set_port_write_redirector (baseport, baseport + 0xE, &outBlaster); + set_port_read_redirector (baseport, baseport + 0xE, &inBlaster); +} |