From: John Tsiombikas Date: Wed, 30 Jan 2019 22:38:56 +0000 (+0200) Subject: initial commit X-Git-Url: http://git.mutantstargoat.com/user/nuclear/?p=dos_auplay;a=commitdiff_plain;h=87ef5613bb9baae3903237321c15133066139f7c;ds=sidebyside initial commit --- 87ef5613bb9baae3903237321c15133066139f7c diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..3fdcdb5 --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +*.obj +*.swp +*.map +*.lnk +*.exe diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..8d9951e --- /dev/null +++ b/Makefile @@ -0,0 +1,42 @@ +src = $(wildcard src/*.c) + +ifeq ($(findstring COMMAND.COM, $(SHELL)), COMMAND.COM) + hostsys = dos + obj = $(subst /,\,$(src:.c=.obj)) + RM = del +else + hostsys = unix + obj = $(src:.c=.obj) +endif +bin = auplay.exe + +opt = -5 +dbg = -d1 +def = -dLITTLEENDIAN + +incpath = -Isrc + +AS = nasm +CC = wcc386 +ASFLAGS = -fobj +CFLAGS = $(dbg) $(opt) $(def) -zq -bt=dos $(incpath) +LDFLAGS = option map +LD = wlink + +$(bin): $(obj) + $(file >objlist.lnk,$(obj)) + $(LD) debug all name $@ system dos4g file { @objlist } $(LDFLAGS) + +%.obj: %.c + $(CC) -fo=$@ $(CFLAGS) $< + +%.obj: %.asm + $(AS) $(ASFLAGS) -o $@ $< + +clean: + $(RM) $(obj) + $(RM) *.obj + $(RM) src\*.obj + $(RM) $(bin) + $(RM) objlist.lnk + $(RM) auplay.map diff --git a/Makefile.wat b/Makefile.wat new file mode 100644 index 0000000..48ce6ac --- /dev/null +++ b/Makefile.wat @@ -0,0 +1,40 @@ +obj = main.obj audio.obj aufile.obj auwav.obj dma.obj au_sb.obj dpmi.obj +bin = auplay.exe + +opt = -5 +dbg = -d1 +def = -dLITTLEENDIAN + +incpath = -Isrc + +!ifdef __UNIX__ +RM = rm -f +!else +RM = del +!endif + +AS = nasm +CC = wcc386 +ASFLAGS = -fobj +CFLAGS = $(dbg) $(opt) $(def) -zq -bt=dos $(incpath) +LDFLAGS = option map +LD = wlink + +$(bin): $(obj) + %write objlist.lnk $(obj) + $(LD) debug all name $@ system dos4g file { @objlist } $(LDFLAGS) + +.c: src +.asm: src + +.c.obj: .autodepend + $(CC) -fo=$@ $(CFLAGS) $[* + +.asm.obj: + $(AS) $(ASFLAGS) -o $@ $[*.asm + +clean: .symbolic + $(RM) *.obj + $(RM) *.lnk + $(RM) *.map + $(RM) $(bin) diff --git a/src/au_sb.c b/src/au_sb.c new file mode 100644 index 0000000..d733f20 --- /dev/null +++ b/src/au_sb.c @@ -0,0 +1,516 @@ +#include +#include +#include +#include +#include +#include "audio.h" +#include "au_sb.h" +#include "dpmi.h" +#include "dma.h" +#include "intr.h" + +#define REG_MIXPORT (base_port + 0x4) +#define REG_MIXDATA (base_port + 0x5) +#define REG_RESET (base_port + 0x6) +#define REG_RDATA (base_port + 0xa) +#define REG_WDATA (base_port + 0xc) +#define REG_WSTAT (base_port + 0xc) +#define REG_RSTAT (base_port + 0xe) +#define REG_INTACK (base_port + 0xe) +#define REG_INT16ACK (base_port + 0xf) + +#define WSTAT_BUSY 0x80 +#define RSTAT_RDY 0x80 + +#define DSP_RATE 0x40 +#define DSP4_OUT_RATE 0x41 +#define DSP4_IN_RATE 0x42 +#define DSP_GET_VER 0xe1 + +/* start DMA playback/recording. combine with fifo/auto/input flags */ +#define DSP4_START_DMA8 0xc0 +#define DSP4_START_DMA16 0xb0 +#define DSP4_FIFO 0x02 +#define DSP4_AUTO 0x04 +#define DSP4_INPUT 0x08 + +/* transfer mode commands */ +#define DSP4_MODE_SIGNED 0x10 +#define DSP4_MODE_STEREO 0x20 + +/* immediately pause/continue */ +#define DSP_PAUSE_DMA8 0xd0 +#define DSP_ENABLE_OUTPUT 0xd1 +#define DSP_DISABLE_OUTPUT 0xd3 +#define DSP_CONT_DMA8 0xd4 +#define DSP_PAUSE_DMA16 0xd5 +#define DSP_CONT_DMA16 0xd6 + +/* end the playback at the end of the current buffer */ +#define DSP_END_DMA16 0xd9 +#define DSP_END_DMA8 0xda + +/* mixer registers */ +#define MIX_MASTER 0x02 +#define MIX_VOICE 0x0a +#define MIX_SBPRO_VOICE 0x04 +#define MIX_SBPRO_MASTER 0x22 +#define MIX_SB16_MASTER_L 0x30 +#define MIX_SB16_MASTER_R 0x31 +#define MIX_SB16_VOICE_L 0x32 +#define MIX_SB16_VOICE_R 0x33 +#define MIX_SB16_IRQ_SEL 0x80 +#define MIX_SB16_DMA_SEL 0x81 + +#define VER_MAJOR(x) ((x) >> 8) +#define VER_MINOR(x) ((x) & 0xff) + +/* delay for about 1us */ +#define iodelay() outp(0x80, 0) + +static void INTERRUPT intr_handler(); +static void start_dsp4(int bits, unsigned int mode, int num_samples); +static void start_dsp(int nchan, int num_samples); +static void write_dsp(unsigned char val); +static unsigned char read_dsp(void); +static void write_mix(unsigned char val, int reg); +static unsigned char read_mix(int reg); +static int get_dsp_version(void); +static int dsp4_detect_irq(void); +static int dsp4_detect_dma(void); +static void print_dsp_info(void); +static const char *sbname(int ver); + +static int base_port; +static int irq, dma_chan, dma16_chan, dsp_ver; +static int dsp4; +static void *buffer; +static int xfer_mode; + +static int isplaying; +static int cur_bits, cur_mode; +static int curblk; + +static void (INTERRUPT *prev_intr_handler)(); + + +int sb_detect(void) +{ + int i; + char *env; + + if((env = getenv("BLASTER"))) { + dma16_chan = -1; + if(sscanf(env, "A%x I%d D%d H%d", &base_port, &irq, &dma_chan, &dma16_chan) >= 3) { + if(sb_reset_dsp() == 0) { + dsp_ver = get_dsp_version(); + dsp4 = VER_MAJOR(dsp_ver) >= 4 ? 1 : 0; + print_dsp_info(); + return 1; + } + } else { + printf("sb_detect: malformed BLASTER environment variable. Fallback to probing.\n"); + } + } + + for(i=0; i<6; i++) { + base_port = 0x200 + ((i + 1) << 4); + if(sb_reset_dsp() == 0) { + dsp_ver = get_dsp_version(); + dsp4 = VER_MAJOR(dsp_ver) >= 4 ? 1 : 0; + + if(dsp4) { + if(dsp4_detect_irq() == -1) { + printf("sb_detect: failed to configure IRQ\n"); + return 0; + } + if(dsp4_detect_dma() == -1) { + printf("sb_detect: failed to configure DMA\n"); + return 0; + } + + } else { + /* XXX for old sound blasters, hard-code to IRQ 5 DMA 1 for now */ + irq = 5; + dma_chan = 1; + dma16_chan = -1; + + printf("sb_detect: old sound blaster dsp. assuming: irq 5, dma 1\n"); + } + print_dsp_info(); + + return 1; + } + } + + return 0; +} + +int sb_reset_dsp(void) +{ + int i; + + outp(REG_RESET, 1); + for(i=0; i<3; i++) iodelay(); + outp(REG_RESET, 0); + + for(i=0; i<128; i++) { + if(inp(REG_RSTAT) & RSTAT_RDY) { + if(inp(REG_RDATA) == 0xaa) { + return 0; + } + } + } + + return -1; +} + +void sb_set_output_rate(int rate) +{ + if(dsp4) { + write_dsp(DSP4_OUT_RATE); + write_dsp(rate >> 8); + write_dsp(rate & 0xff); + } else { + int tcon = 256 - 1000000 / rate; + write_dsp(DSP_RATE); + write_dsp(tcon); + } +} + +void *sb_buffer(int *size) +{ + *size = 32768; + return (char*)buffer + curblk * 32768; +} + +void sb_start(int rate, int bits, int nchan) +{ + uint16_t seg, pmsel; + uint32_t addr; + int size; + + if(!buffer) { + /* allocate a 64k-aligned 64k buffer in low memory */ + if(!(seg = dpmi_alloc(65536 * 2 / 16, &pmsel))) { + fprintf(stderr, "sb_start: failed to allocate DMA buffer\n"); + return; + } + + printf("DBG allocated seg: %x, addr: %lx\n", (unsigned int)seg, + (unsigned long)seg << 4); + + addr = ((uint32_t)(seg << 4) + 0xffff) & 0xffff0000; + printf("DBG aligned: %lx\n", (unsigned long)addr); + buffer = (void*)addr; + } else { + addr = (uint32_t)buffer; + } + + if(!(size = audio_callback(buffer, 32768))) { + return; + } + curblk = 1; + + _disable(); + if(!prev_intr_handler) { + prev_intr_handler = _dos_getvect(IRQ_TO_INTR(irq)); + } + printf("setting interrupt vector: %d\n", IRQ_TO_INTR(irq)); + _dos_setvect(IRQ_TO_INTR(irq), intr_handler); + _enable(); + + unmask_irq(irq); + + cur_bits = bits; + cur_mode = 0; /* TODO */ + if(nchan > 1) { + cur_mode |= DSP4_MODE_STEREO; + } + + write_dsp(DSP_ENABLE_OUTPUT); + dma_out(dma_chan, addr, size, DMA_SINGLE); + sb_set_output_rate(rate); + start_dsp4(cur_bits, cur_mode, size); + isplaying = 1; +} + +static void start_dsp4(int bits, unsigned int mode, int num_samples) +{ + unsigned char cmd = bits == 8 ? DSP4_START_DMA8 : DSP4_START_DMA16; + assert(bits == 8 || bits == 16); + + /*cmd |= DSP4_AUTO | DSP4_FIFO;*/ + + /* program the DSP to start the DMA transfer */ + write_dsp(cmd); + write_dsp(mode); + num_samples--; + write_dsp(num_samples & 0xff); + write_dsp((num_samples >> 8) & 0xff); +} + +static void start_dsp(int nchan, int num_samples) +{ + /* program the DSP to start the DMA transfer */ + /* TODO */ +} + +void sb_pause(void) +{ + write_dsp(cur_bits == 8 ? DSP_PAUSE_DMA8 : DSP_PAUSE_DMA16); +} + +void sb_continue(void) +{ + write_dsp(cur_bits == 8 ? DSP_CONT_DMA8 : DSP_CONT_DMA16); +} + +void sb_stop(void) +{ + write_dsp(DSP_DISABLE_OUTPUT); + + mask_irq(irq); + /* TODO: don't _enable, restore state */ + _disable(); + _dos_setvect(IRQ_TO_INTR(irq), prev_intr_handler); + _enable(); + + /* no need to stop anything if we're in single-cycle mode */ + if(cur_mode & DSP4_AUTO) { + write_dsp(cur_bits == 8 ? DSP_END_DMA8 : DSP_END_DMA16); + } + isplaying = 0; +} + +int sb_isplaying(void) +{ + return isplaying; +} + +void sb_volume(int vol) +{ + /* TODO */ +} + +static void INTERRUPT intr_handler() +{ + int size; + void *bptr = (unsigned char*)buffer + curblk * 32768; + + curblk = (curblk + 1) & 1; + + /* ask for more data */ + if(!(size = audio_callback(bptr, 32768))) { + sb_stop(); + } else { + dma_out(dma_chan, (uint32_t)bptr, size, DMA_SINGLE); + start_dsp4(cur_bits, cur_mode, size); + } + + /* acknowledge the interrupt */ + inp(REG_INTACK); + + if(irq > 7) { + outp(PIC2_CMD, OCW2_EOI); + } + outp(PIC1_CMD, OCW2_EOI); +} + +static void write_dsp(unsigned char val) +{ + while(inp(REG_WSTAT) & WSTAT_BUSY); + outp(REG_WDATA, val); +} + +static unsigned char read_dsp(void) +{ + while((inp(REG_RSTAT) & RSTAT_RDY) == 0); + return inp(REG_RDATA); +} + +static void write_mix(unsigned char val, int reg) +{ + outp(REG_MIXPORT, reg); + outp(REG_MIXDATA, val); +} + +static unsigned char read_mix(int reg) +{ + outp(REG_MIXPORT, reg); + return inp(REG_MIXDATA); +} + +static int get_dsp_version(void) +{ + int major, minor; + + write_dsp(DSP_GET_VER); + major = read_dsp(); + minor = read_dsp(); + + return (major << 8) | minor; +} + +static int dsp4_detect_irq(void) +{ + int i, irqsel; + static int irqtab[] = {2, 5, 7, 10}; + + irq = 0; + irqsel = read_mix(MIX_SB16_IRQ_SEL); + for(i=0; i<4; i++) { + if(irqsel & (1 << i)) { + irq = irqtab[i]; + break; + } + } + if(!irq) { + /* try to force IRQ 5 */ + write_mix(2, MIX_SB16_IRQ_SEL); /* bit1 selects irq 5 */ + + /* re-read to verify */ + irqsel = read_mix(MIX_SB16_IRQ_SEL); + if(irqsel != 2) { + return -1; + } + irq = 5; + } + + return irq; +} + +static int dsp4_detect_dma(void) +{ + int i, dmasel, tmp; + static int dmatab[] = {0, 1, -1, 3, -1, 5, 6, 7}; + + dma_chan = -1; + dma16_chan = -1; + dmasel = read_mix(MIX_SB16_DMA_SEL); + for(i=0; i<4; i++) { + if(dmasel & (1 << i)) { + dma_chan = dmatab[i]; + break; + } + } + for(i=5; i<8; i++) { + if(dmasel & (1 << i)) { + dma16_chan = dmatab[i]; + break; + } + } + if(dma_chan == -1) { + /* try to force DMA 1 */ + dmasel |= 2; + } + if(dma16_chan == -1) { + /* try to force 16bit DMA 5 */ + dmasel |= 0x20; + } + + if(dma_chan == -1 || dma16_chan == -1) { + write_mix(dmasel, MIX_SB16_DMA_SEL); + + /* re-read to verify */ + tmp = read_mix(MIX_SB16_DMA_SEL); + if(tmp != dmasel) { + return -1; + } + dma_chan = 1; + dma16_chan = 5; + } + + return dma_chan; +} + +static void print_dsp_info(void) +{ + int master[2], voice[2]; + int val; + + printf("sb_detect: %s (DSP v%d.%02d) port %xh, irq %d", sbname(dsp_ver), + VER_MAJOR(dsp_ver), VER_MINOR(dsp_ver), base_port, irq); + if(VER_MAJOR(dsp_ver) >= 4) { + printf(" dma %d/%d\n", dma_chan, dma16_chan); + } else { + printf(" dma %d\n", dma_chan); + } + + if(VER_MAJOR(dsp_ver) >= 4) { + master[0] = 100 * (int)(read_mix(MIX_SB16_MASTER_L) >> 3) / 31; + master[1] = 100 * (int)(read_mix(MIX_SB16_MASTER_R) >> 3) / 31; + voice[0] = 100 * (int)(read_mix(MIX_SB16_VOICE_L) >> 3) / 31; + voice[1] = 100 * (int)(read_mix(MIX_SB16_VOICE_R) >> 3) / 31; + } else if(VER_MAJOR(dsp_ver) >= 3) { + val = read_mix(MIX_SBPRO_MASTER); + master[0] = 100 * (val >> 5) / 7; + master[1] = 100 * ((val >> 1) & 7) / 7; + val = read_mix(MIX_SBPRO_VOICE); + voice[0] = 100 * (val >> 5) / 7; + voice[1] = 100 * ((val >> 1) & 7) / 7; + } else { + val = read_mix(MIX_MASTER); + master[0] = master[1] = 100 * ((val >> 1) & 7) / 7; + val = read_mix(MIX_VOICE); + voice[0] = voice[1] = 100 * ((val >> 1) & 3) / 3; + } + printf(" mixer: master(%d|%d) voice(%d|%d)\n", master[0], master[1], + voice[0], voice[1]); +} + +#define V(maj, min) (((maj) << 8) | (min)) + +static const char *sbname(int ver) +{ + int major = VER_MAJOR(ver); + int minor = VER_MINOR(ver); + + switch(major) { + case 1: + if(minor == 5) { + return "Sound Blaster 1.5"; + } + return "Sound Blaster 1.0"; + + case 2: + if(minor == 1 || minor == 2) { + return "Sound Blaster 2.0"; + } + break; + + case 3: + switch(minor) { + case 0: + return "Sound Blaster Pro"; + case 1: + case 2: + return "Sound Blaster Pro 2"; + case 5: + return "Gallant SC-6000"; + default: + break; + } + break; + + case 4: + switch(minor) { + case 4: + case 5: + return "Sound Blaster 16"; + case 11: + return "Sound Blaster 16 SCSI-2"; + case 12: + return "Sound Blaster AWE 32"; + case 13: + return "Sound Blaster ViBRA16C"; + case 16: + return "Sound Blaster AWE 64"; + default: + break; + } + break; + } + + return "Unknown Sound Blaster"; +} diff --git a/src/au_sb.h b/src/au_sb.h new file mode 100644 index 0000000..4766b44 --- /dev/null +++ b/src/au_sb.h @@ -0,0 +1,26 @@ +#ifndef AU_SB_H_ +#define AU_SB_H_ + +/* returns true (nonzero) if a sound blaster DSP is detected in the ISA bus + * and sets the internal base_port so that subsequent calls can find it + */ +int sb_detect(void); + +/* returns 0 for success, non-zero if the DSP isn't responding at the currently + * selected base port + */ +int sb_reset_dsp(void); + +void *sb_buffer(int *size); + +void sb_set_output_rate(int rate); + +void sb_start(int rate, int bits, int nchan); +void sb_pause(void); +void sb_continue(void); +void sb_stop(void); +int sb_isplaying(void); + +void sb_volume(int vol); + +#endif /* AU_SB_H_ */ diff --git a/src/audio.c b/src/audio.c new file mode 100644 index 0000000..88b8400 --- /dev/null +++ b/src/audio.c @@ -0,0 +1,78 @@ +#include +#include "audio.h" +#include "au_sb.h" + +struct audrv { + void *(*get_buffer)(int *size); + void (*start)(int rate, int bits, int nchan); + void (*pause)(void); + void (*cont)(void); + void (*stop)(void); + void (*volume)(int vol); + int (*isplaying)(void); +}; + +static struct audrv drv; + +static audio_callback_func cbfunc; +static void *cbcls; + +void audio_init(void) +{ + if(sb_detect()) { + drv.get_buffer = sb_buffer; + drv.start = sb_start; + drv.pause = sb_pause; + drv.cont = sb_continue; + drv.stop = sb_stop; + drv.volume = sb_volume; + drv.isplaying = sb_isplaying; + return; + } + + printf("No supported audio device detected\n"); +} + +void audio_set_callback(audio_callback_func func, void *cls) +{ + cbfunc = func; + cbcls = cls; +} + +int audio_callback(void *buf, int sz) +{ + if(!cbfunc) { + return 0; + } + return cbfunc(buf, sz, cbcls); +} + +void audio_play(int rate, int bits, int nchan) +{ + drv.start(rate, bits, nchan); +} + +void audio_pause(void) +{ + drv.pause(); +} + +void audio_resume(void) +{ + drv.cont(); +} + +void audio_stop(void) +{ + drv.stop(); +} + +void audio_volume(int vol) +{ + drv.volume(vol); +} + +int audio_isplaying(void) +{ + return drv.isplaying(); +} diff --git a/src/audio.h b/src/audio.h new file mode 100644 index 0000000..c522134 --- /dev/null +++ b/src/audio.h @@ -0,0 +1,19 @@ +#ifndef AUDIO_H_ +#define AUDIO_H_ + +typedef int (*audio_callback_func)(void *buffer, int size, void *cls); + +void audio_init(void); + +void audio_set_callback(audio_callback_func func, void *cls); +int audio_callback(void *buf, int sz); + +void audio_play(int rate, int bits, int nchan); +void audio_pause(void); +void audio_resume(void); +void audio_stop(void); +int audio_isplaying(void); + +void audio_volume(int vol); + +#endif /* AUDIO_H_ */ diff --git a/src/aufile.c b/src/aufile.c new file mode 100644 index 0000000..b67925b --- /dev/null +++ b/src/aufile.c @@ -0,0 +1,51 @@ +#include +#include +#include +#include +#include "aufile.h" + +int au_open_wav(struct au_file *au); + +struct au_file *au_open(const char *fname) +{ + FILE *fp; + struct au_file *au; + + if(!(fp = fopen(fname, "rb"))) { + fprintf(stderr, "au_open: failed to open file: %s: %s\n", fname, strerror(errno)); + return 0; + } + + if(!(au = malloc(sizeof *au))) { + fclose(fp); + fprintf(stderr, "au_open: failed to allocate file structure: %s\n", strerror(errno)); + return 0; + } + au->fp = fp; + + if(au_open_wav(au) != -1) { + return au; + } + + fprintf(stderr, "au_open: invalid file: %s\n", fname); + fclose(fp); + free(au); + return 0; +} + +void au_close(struct au_file *au) +{ + au->close(au); + fclose(au->fp); + free(au); +} + +void au_reset(struct au_file *au) +{ + au->reset(au); +} + +int au_read(struct au_file *au, void *buf, int size) +{ + return au->read(au, buf, size); +} diff --git a/src/aufile.h b/src/aufile.h new file mode 100644 index 0000000..faca4c8 --- /dev/null +++ b/src/aufile.h @@ -0,0 +1,20 @@ +#ifndef AUFILE_H_ +#define AUFILE_H_ + +struct au_file { + int rate, bits, chan; + void *data; + + FILE *fp; + void (*close)(struct au_file*); + void (*reset)(struct au_file*); + int (*read)(struct au_file*, void*, int); +}; + +struct au_file *au_open(const char *fname); +void au_close(struct au_file *au); + +void au_reset(struct au_file *au); +int au_read(struct au_file *au, void *buf, int size); + +#endif /* AUFILE_H_ */ diff --git a/src/auwav.c b/src/auwav.c new file mode 100644 index 0000000..eca5a76 --- /dev/null +++ b/src/auwav.c @@ -0,0 +1,146 @@ +#include +#include +#include "aufile.h" +#include "inttypes.h" + +struct format { + uint16_t fmt; + uint16_t nchan; + uint32_t rate; + uint16_t avgbaud; + uint16_t block_align; + uint16_t sample_bytes; +}; + +struct playback_data { + uint32_t start, size; + uint32_t bytes_left; +}; + +#define FOURCC(a, b, c, d) \ + ((uint32_t)(a) | ((uint32_t)(b) << 8) | ((uint32_t)(c) << 16) | ((uint32_t)(d) << 24)) + +enum { + ID_RIFF = FOURCC('R', 'I', 'F', 'F'), + ID_WAVE = FOURCC('W', 'A', 'V', 'E'), + ID_FMT = FOURCC('f', 'm', 't', ' '), + ID_DATA = FOURCC('d', 'a', 't', 'a') +}; + +static void close_wav(struct au_file *au); +static void reset_wav(struct au_file *au); +static int read_wav(struct au_file *au, void *buf, int size); +static int read_uint32(uint32_t *res, FILE *fp); +static int read_format(struct format *fmt, int fmtsize, FILE *fp); + + +int au_open_wav(struct au_file *au) +{ + uint32_t id, len; + struct format fmt; + struct playback_data *pb; + + if(read_uint32(&id, au->fp) == -1 || id != ID_RIFF) + return -1; + fseek(au->fp, 4, SEEK_CUR); + if(read_uint32(&id, au->fp) == -1 || id != ID_WAVE) + return -1; + if(read_uint32(&id, au->fp) == -1 || id != ID_FMT) + return -1; + if(read_uint32(&len, au->fp) == -1) + return -1; + if(read_format(&fmt, len, au->fp) == -1) + return -1; + if(read_uint32(&id, au->fp) == -1 || id != ID_DATA) + return -1; + if(read_uint32(&len, au->fp) == -1) + return -1; + + if(!(pb = malloc(sizeof *pb))) { + fprintf(stderr, "failed to allocate wav playback data block\n"); + return -1; + } + pb->start = ftell(au->fp); + pb->size = pb->bytes_left = len; + + au->rate = fmt.rate; + au->bits = fmt.sample_bytes * 8; + au->chan = fmt.nchan; + au->data = pb; + + au->close = close_wav; + au->reset = reset_wav; + au->read = read_wav; + return 0; +} + +static void close_wav(struct au_file *au) +{ + free(au->data); +} + +static void reset_wav(struct au_file *au) +{ + struct playback_data *pb = au->data; + pb->bytes_left = pb->size; + fseek(au->fp, pb->start, SEEK_SET); +} + +static int read_wav(struct au_file *au, void *buf, int size) +{ + struct playback_data *pb = au->data; + size_t rd; + + if(size > pb->bytes_left) { + size = pb->bytes_left; + } + if(size <= 0) { + return 0; + } + if((rd = fread(buf, 1, size, au->fp)) == -1) { + pb->bytes_left = 0; + return -1; + } + pb->bytes_left -= rd; + return rd; +} + +#ifdef BIGENDIAN +static void swap_uint32(uint32_t *val) +{ + uint32_t x = *val; + *val = (x << 24) | ((x & 0xff00) << 8) | ((x & 0xff0000) >> 8) | (x >> 24); +} + +static void swap_uint16(uint16_t *val) +{ + uint16_t x = *val; + *val = (x << 8) | (x >> 8); +} +#endif + +static int read_uint32(uint32_t *res, FILE *fp) +{ + if(fread(res, 4, 1, fp) < 1) { + return -1; + } +#ifdef BIGENDIAN + swap_uint32(res); +#endif + return 0; +} + +static int read_format(struct format *fmt, int fmtsize, FILE *fp) +{ + if(fread(fmt, 1, fmtsize, fp) < fmtsize) { + return -1; + } +#ifdef BIGENDIAN + swap_uint16(&fmt->fmt); + swap_uint16(&fmt->nchan); + swap_uint32(&fmt->rate); + swap_uint16(&fmt->avgbaud); + swap_uint16(&fmt->block_align); +#endif + return 0; +} diff --git a/src/dma.c b/src/dma.c new file mode 100644 index 0000000..356836a --- /dev/null +++ b/src/dma.c @@ -0,0 +1,133 @@ +#include +#include "dma.h" + +/* 8bit DMA ports */ +#define DMA_0_ADDR 0x00 +#define DMA_0_COUNT 0x01 +#define DMA_1_ADDR 0x02 +#define DMA_1_COUNT 0x03 +#define DMA_2_ADDR 0x04 +#define DMA_2_COUNT 0x05 +#define DMA_3_ADDR 0x06 +#define DMA_3_COUNT 0x07 +/* 16bit DMA ports */ +#define DMA_4_ADDR 0xc0 +#define DMA_4_COUNT 0xc2 +#define DMA_5_ADDR 0xc4 +#define DMA_5_COUNT 0xc6 +#define DMA_6_ADDR 0xc8 +#define DMA_6_COUNT 0xca +#define DMA_7_ADDR 0xcc +#define DMA_7_COUNT 0xce + +#define DMA_ADDR(c) \ + ((c < 4) ? DMA_0_ADDR + ((c) << 1) : (DMA_4_ADDR + ((c) << 2))) +#define DMA_COUNT(c) \ + ((c < 4) ? DMA_0_COUNT + ((c) << 1) : (DMA_4_COUNT + ((c) << 2))) + +#define DMA8_MASK 0x0a +#define DMA8_MODE 0x0b +#define DMA8_CLR_FLIPFLOP 0x0c +#define DMA8_RESET 0x0d +#define DMA8_MASK_RST 0x0e +#define DMA8_RMASK 0x0f +#define DMA16_MASK 0xd4 +#define DMA16_MODE 0xd6 +#define DMA16_CLR_FLIPFLOP 0xd8 +#define DMA16_RESET 0xda +#define DMA16_MASK_RST 0xdc +#define DMA16_RMASK 0xde + +#define DMA_MASK(c) ((c) < 4 ? DMA8_MASK : DMA16_MASK) +#define DMA_MODE(c) ((c) < 4 ? DMA8_MODE : DMA16_MODE) +#define DMA_CLR_FLIPFLOP(c) ((c) < 4 ? DMA8_CLR_FLIPFLOP : DMA16_CLR_FLIPFLOP) +#define DMA_RESET(c) ((c) < 4 ? DMA8_RESET : DMA16_RESET) +#define DMA_MASK_RST(c) ((c) < 4 ? DMA8_MASK_RST : DMA16_MASK_RST) +#define DMA_RMASK(c) ((c) < 4 ? DMA8_RMASK : DMA16_RMASK) + +#define DMA_0_PAGE 0x87 +#define DMA_1_PAGE 0x83 +#define DMA_2_PAGE 0x81 +#define DMA_3_PAGE 0x82 +#define DMA_4_PAGE 0x8f +#define DMA_5_PAGE 0x8b +#define DMA_6_PAGE 0x89 +#define DMA_7_PAGE 0x8a + +#define MODE_CHAN(x) ((x) & 3) +#define MODE_WRITE 0x04 +#define MODE_READ 0x08 +#define MODE_AUTO 0x10 +#define MODE_DECR 0x20 +#define MODE_SINGLE 0x40 +#define MODE_BLOCK 0x80 +#define MODE_CASCADE 0xc0 + +#define MASK_CHAN(x) ((x) & 3) +#define MASK_DISABLE 0x04 + +#define RMASK_CHAN(x) (1 << ((x) & 3)) + +#define IS_16BIT(c) ((c) >= 4) + +static void dma_io(int chan, uint32_t phyaddr, int size, unsigned int flags, unsigned int dir); +static __inline void mask(int chan); +static __inline void unmask(int chan); + +static int page_port[] = { + DMA_0_PAGE, DMA_1_PAGE, DMA_2_PAGE, DMA_3_PAGE, + DMA_4_PAGE, DMA_5_PAGE, DMA_6_PAGE, DMA_7_PAGE +}; + +void dma_out(int chan, uint32_t phyaddr, int size, unsigned int flags) +{ + dma_io(chan, phyaddr, size, flags, MODE_READ); +} + +void dma_in(int chan, uint32_t phyaddr, int size, unsigned int flags) +{ + dma_io(chan, phyaddr, size, flags, MODE_WRITE); +} + +static void dma_io(int chan, uint32_t phyaddr, int size, unsigned int flags, unsigned int dir) +{ + unsigned int mode; + int addr_port, count_port; + + addr_port = DMA_ADDR(chan); + count_port = DMA_COUNT(chan); + + mask(chan); + outp(DMA_CLR_FLIPFLOP(chan), 0); + + /* first 2 bits of flags correspond to the mode bits 6,7 */ + mode = ((flags & 3) << 6) | dir | MODE_CHAN(chan); + if(flags & DMA_DECR) mode |= MODE_DECR; + if(flags & DMA_AUTO) mode |= MODE_AUTO; + outp(DMA_MODE(chan), mode); + + if(IS_16BIT(chan)) { + phyaddr >>= 1; + size >>= 1; + } + + outp(addr_port, phyaddr & 0xff); + outp(addr_port, (phyaddr >> 8) & 0xff); + outp(page_port[chan], (phyaddr >> 16) & 0xff); + + size--; + outp(count_port, size & 0xff); + outp(count_port, (size >> 8) & 0xff); + + unmask(chan); +} + +static __inline void mask(int chan) +{ + outp(DMA_MASK(chan), MASK_CHAN(chan) | MASK_DISABLE); +} + +static __inline void unmask(int chan) +{ + outp(DMA_MASK(chan), MASK_CHAN(chan)); +} diff --git a/src/dma.h b/src/dma.h new file mode 100644 index 0000000..93cfa5d --- /dev/null +++ b/src/dma.h @@ -0,0 +1,17 @@ +#ifndef DMA_H_ +#define DMA_H_ + +#include + +enum { + DMA_SINGLE = 0x01, + DMA_BLOCK = 0x02, + DMA_CASCADE = DMA_SINGLE | DMA_BLOCK, + DMA_DECR = 0x08, + DMA_AUTO = 0x10 +}; + +void dma_out(int chan, uint32_t phyaddr, int size, unsigned int flags); +void dma_in(int chan, uint32_t phyaddr, int size, unsigned int flags); + +#endif /* DMA_H_ */ diff --git a/src/dpmi.c b/src/dpmi.c new file mode 100644 index 0000000..7d2c2b5 --- /dev/null +++ b/src/dpmi.c @@ -0,0 +1,55 @@ +#include "dpmi.h" + +void dpmi_real_int(int inum, struct dpmi_real_regs *regs) +{ + unsigned char int_num = (unsigned char)inum; + __asm { + mov eax, 0x300 + mov edi, regs + mov bl, int_num + mov bh, 0 + xor ecx, ecx + int 0x31 + } +} + +void *dpmi_mmap(uint32_t phys_addr, unsigned int size) +{ + uint16_t mem_high, mem_low; + uint16_t phys_high = phys_addr >> 16; + uint16_t phys_low = phys_addr & 0xffff; + uint16_t size_high = size >> 16; + uint16_t size_low = size & 0xffff; + unsigned int err, res = 0; + + __asm { + mov eax, 0x800 + mov bx, phys_high + mov cx, phys_low + mov si, size_high + mov di, size_low + int 0x31 + add res, 1 + mov err, eax + mov mem_high, bx + mov mem_low, cx + } + + if(res == 2) { + return 0; + } + return (void*)(((uint32_t)mem_high << 16) | ((uint32_t)mem_low)); +} + +void dpmi_munmap(void *addr) +{ + uint16_t mem_high = (uint32_t)addr >> 16; + uint16_t mem_low = (uint16_t)addr; + + __asm { + mov eax, 0x801 + mov bx, mem_high + mov cx, mem_low + int 0x31 + } +} diff --git a/src/dpmi.h b/src/dpmi.h new file mode 100644 index 0000000..5f06823 --- /dev/null +++ b/src/dpmi.h @@ -0,0 +1,34 @@ +#ifndef DPMI_H_ +#define DPMI_H_ + +#include "inttypes.h" + +struct dpmi_real_regs { + uint32_t edi, esi, ebp; + uint32_t reserved; + uint32_t ebx, edx, ecx, eax; + uint16_t flags; + uint16_t es, ds, fs, gs; + uint16_t ip, cs, sp, ss; +}; + +uint16_t dpmi_alloc(unsigned int par, uint16_t *sel); +void dpmi_free(uint16_t sel); + +#pragma aux dpmi_alloc = \ + "mov eax, 0x100" \ + "int 0x31" \ + "mov [edi], dx" \ + value[ax] parm[ebx][edi]; + +#pragma aux dpmi_free = \ + "mov eax, 0x101" \ + "int 0x31" \ + parm[dx]; + +void dpmi_real_int(int inum, struct dpmi_real_regs *regs); + +void *dpmi_mmap(uint32_t phys_addr, unsigned int size); +void dpmi_munmap(void *addr); + +#endif /* DPMI_H_ */ diff --git a/src/intr.h b/src/intr.h new file mode 100644 index 0000000..3345324 --- /dev/null +++ b/src/intr.h @@ -0,0 +1,60 @@ +#ifndef INTR_H_ +#define INTR_H_ + +#define INTERRUPT __interrupt __far + +#define IRQ_TO_INTR(x) ((x) + 8) + +/* PIC command and data ports */ +#define PIC1_CMD 0x20 +#define PIC1_DATA 0x21 +#define PIC2_CMD 0xa0 +#define PIC2_DATA 0xa1 +/* PIC operation command word 2 bits */ +#define OCW2_EOI (1 << 5) + +unsigned char get_irq_mask(int pic); +#pragma aux get_irq_mask = \ + "cmp bx, 0" \ + "jnz getpic2" \ + "in al, 0x21" \ + "jmp getend" \ + "getpic2:" \ + "in al, 0xa1" \ + "getend:" \ + value[al] parm[ebx]; + +void mask_irq(int irq); +#pragma aux mask_irq = \ + "mov dx, 0x21" \ + "cmp ax, 8" \ + "jb skip_mask_pic2" \ + "mov dx, 0xa1" \ + "sub ax, 8" \ + "skip_mask_pic2:" \ + "mov cl, al" \ + "mov ah, 1" \ + "shl ah, cl" \ + "in al, dx" \ + "or al, ah" \ + "out dx, al" \ + parm[eax] modify[cl dx]; + +void unmask_irq(int irq); +#pragma aux unmask_irq = \ + "mov dx, 0x21" \ + "cmp ax, 8" \ + "jb skip_unmask_pic2" \ + "mov dx, 0xa1" \ + "sub ax, 8" \ + "skip_unmask_pic2:" \ + "mov cl, al" \ + "mov ah, 1" \ + "shl ah, cl" \ + "not ah" \ + "in al, dx" \ + "and al, ah" \ + "out dx, al" \ + parm[eax] modify[cl dx]; + +#endif /* INTR_H_ */ diff --git a/src/inttypes.h b/src/inttypes.h new file mode 100644 index 0000000..1742166 --- /dev/null +++ b/src/inttypes.h @@ -0,0 +1,18 @@ +#ifndef INT_TYPES_H_ +#define INT_TYPES_H_ + +#if defined(__DOS__) || defined(WIN32) +typedef char int8_t; +typedef short int16_t; +typedef long int32_t; + +typedef unsigned char uint8_t; +typedef unsigned short uint16_t; +typedef unsigned long uint32_t; + +typedef unsigned long intptr_t; +#else +#include +#endif + +#endif /* INT_TYPES_H_ */ diff --git a/src/main.c b/src/main.c new file mode 100644 index 0000000..2015a10 --- /dev/null +++ b/src/main.c @@ -0,0 +1,47 @@ +#include +#include +#include "aufile.h" + +static int play_file(const char *fname); +static void print_usage(const char *argv0); + +int main(int argc, char **argv) +{ + int i; + + play_file("namarie.wav"); /* TODO remove */ + + for(i=1; i ... \n", argv0); + printf("options:\n"); + printf(" -h,-help: print usage and exit\n"); +}