initial commit
authorJohn Tsiombikas <nuclear@mutantstargoat.com>
Thu, 8 Nov 2018 06:24:17 +0000 (08:24 +0200)
committerJohn Tsiombikas <nuclear@mutantstargoat.com>
Thu, 8 Nov 2018 06:24:17 +0000 (08:24 +0200)
.gitignore [new file with mode: 0644]
Makefile [new file with mode: 0644]
src/au_sb.c [new file with mode: 0644]
src/au_sb.h [new file with mode: 0644]
src/audio.c [new file with mode: 0644]
src/audio.h [new file with mode: 0644]
src/dma.c [new file with mode: 0644]
src/dma.h [new file with mode: 0644]
src/main.c [new file with mode: 0644]

diff --git a/.gitignore b/.gitignore
new file mode 100644 (file)
index 0000000..d81386e
--- /dev/null
@@ -0,0 +1,3 @@
+*.o
+*.d
+*.swp
diff --git a/Makefile b/Makefile
new file mode 100644 (file)
index 0000000..cb6a3d6
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,55 @@
+src = $(wildcard src/*.c)
+ssrc = $(wildcard src/*.s)
+asmsrc = $(wildcard src/*.asm)
+obj = $(src:.c=.o) $(ssrc:.s=.o) $(asmsrc:.asm=.o)
+dep = $(src:.c=.d)
+bin = sbtest.exe
+
+ifeq ($(findstring COMMAND.COM, $(SHELL)), COMMAND.COM)
+       hostsys = dos
+else
+       hostsys = unix
+       TOOLPREFIX = i586-pc-msdosdjgpp-
+endif
+
+inc = -Isrc
+opt = -O3 -ffast-math -fno-strict-aliasing
+dbg = -g
+warn = -pedantic -Wall -Wno-unused-function -Wno-unused-variable
+
+CC = $(TOOLPREFIX)gcc
+AR = $(TOOLPREFIX)ar
+CFLAGS = $(warn) -march=pentium $(dbg) $(opt) $(inc)
+LDFLAGS =
+
+$(bin): $(obj)
+       $(CC) -o $@ -Wl,-Map=ld.map $(obj) $(LDFLAGS)
+
+%.o: %.asm
+       nasm -f coff -o $@ $<
+
+-include $(dep)
+
+%.o: %.c
+       $(CC) $(CFLAGS) -o $@ -c $<
+
+%.d: %.c
+       @echo "gen dep $< -> $@" && $(CPP) $(CFLAGS) $< -MM -MT $(@:.d=.o) >$@
+
+.PHONY: clean
+.PHONY: cleandep
+
+ifeq ($(hostsys), dos)
+clean:
+       del src\*.o
+       del $(bin)
+
+cleandep:
+       del src\*.d
+else
+clean:
+       rm -f $(obj) $(bin)
+
+cleandep:
+       rm -f $(dep)
+endif
diff --git a/src/au_sb.c b/src/au_sb.c
new file mode 100644 (file)
index 0000000..ab12b9e
--- /dev/null
@@ -0,0 +1,420 @@
+#include <stdio.h>
+#include <dos.h>
+#include <pc.h>
+#include <go32.h>
+#include <dpmi.h>
+#include "audio.h"
+#include "au_sb.h"
+#include "dma.h"
+
+#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)
+
+#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 CMD_RATE                       0x40
+#define CMD_SB16_OUT_RATE      0x41
+#define CMD_SB16_IN_RATE       0x42
+#define CMD_GET_VER                    0xe1
+
+/* start DMA playback/recording. combine with fifo/auto/input flags */
+#define CMD_START_DMA8         0xc0
+#define CMD_START_DMA16                0xb0
+#define CMD_FIFO                       0x02
+#define CMD_AUTO                       0x04
+#define CMD_INPUT                      0x08
+
+/* immediately pause/continue */
+#define CMD_PAUSE_DMA8         0xd0
+#define CMD_ENABLE_OUTPUT      0xd1
+#define CMD_DISABLE_OUTPUT     0xd3
+#define CMD_CONT_DMA8          0xd4
+#define CMD_PAUSE_DMA16                0xd5
+#define CMD_CONT_DMA16         0xd6
+
+/* end the playback at the end of the current buffer */
+#define CMD_END_DMA16          0xd9
+#define CMD_END_DMA8           0xda
+
+/* transfer mode commands */
+#define CMD_MODE_SIGNED                0x10
+#define CMD_MODE_STEREO                0x20
+
+/* mixer registers */
+#define MIX_IRQ_SEL                    0x80
+#define MIX_DMA_SEL                    0x81
+
+#define VER_MAJOR(x)   ((x) >> 8)
+#define VER_MINOR(x)   ((x) & 0xff)
+
+/* delay for about 1us */
+#define iodelay()      outportb(0x80, 0)
+
+static void intr_handler();
+static void start_dma_transfer(uint32_t addr, int size);
+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 sb16_detect_irq(void);
+static int sb16_detect_dma(void);
+static const char *sbname(int ver);
+
+extern unsigned char low_mem_buffer[];
+
+static int base_port;
+static int irq, dma_chan, dma16_chan;
+static int sb16;
+static void *buffer;
+static int xfer_mode;
+
+int sb_detect(void)
+{
+       int i, ver;
+
+       for(i=0; i<6; i++) {
+               base_port = 0x200 + ((i + 1) << 4);
+               if(sb_reset_dsp() == 0) {
+                       ver = get_dsp_version();
+                       sb16 = VER_MAJOR(ver) >= 4;
+
+                       if(sb16) {
+                               if(sb16_detect_irq() == -1) {
+                                       printf("sb_detect: failed to configure IRQ\n");
+                                       return 0;
+                               }
+                               if(sb16_detect_dma() == -1) {
+                                       printf("sb_detect: failed to configure DMA\n");
+                                       return 0;
+                               }
+
+                               printf("sb_detect: found %s (DSP v%d.%02d) at port %xh, irq %d, dma %d/%d\n",
+                                               sbname(ver), VER_MAJOR(ver), VER_MINOR(ver),
+                                               base_port, irq, dma_chan, dma16_chan);
+
+                       } 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: found %s (DSP v%d.%02d) at port %xh\n", sbname(ver),
+                                               VER_MAJOR(ver), VER_MINOR(ver), base_port);
+                               printf("sb_detect: old sound blaster dsp. assuming: irq 5, dma 1\n");
+                       }
+
+                       return 1;
+               }
+       }
+
+       return 0;
+}
+
+int sb_reset_dsp(void)
+{
+       int i;
+
+       outportb(REG_RESET, 1);
+       for(i=0; i<3; i++) iodelay();
+       outportb(REG_RESET, 0);
+
+       for(i=0; i<128; i++) {
+               if(inportb(REG_RSTAT) & RSTAT_RDY) {
+                       if(inportb(REG_RDATA) == 0xaa) {
+                               return 0;
+                       }
+               }
+       }
+
+       return -1;
+}
+
+void sb_set_output_rate(int rate)
+{
+       if(sb16) {
+               write_dsp(CMD_SB16_OUT_RATE);
+               write_dsp(rate >> 8);
+               write_dsp(rate & 0xff);
+       } else {
+               int tcon = 256 - 1000000 / rate;
+               write_dsp(CMD_RATE);
+               write_dsp(tcon);
+       }
+}
+
+void *sb_buffer(int *size)
+{
+       *size = 65536;
+       return buffer;
+}
+
+void sb_start(int rate, int nchan)
+{
+       uint32_t addr;
+       int size;
+       _go32_dpmi_seginfo intr;
+
+       /* for now just use the are after boot2. it's only used by the second stage
+        * loader and the VBE init code, none of which should overlap with audio playback.
+        * It's not necessary to use low memory. We can use up to the first 16mb for this,
+        * so if this becomes an issue, I'll make a DMA buffer allocator over 1mb.
+        * start the buffer from the next 64k boundary.
+        */
+       addr = ((uint32_t)low_mem_buffer + 0xffff) & 0xffff0000;
+       buffer = (void*)addr;
+
+       xfer_mode = CMD_MODE_SIGNED;
+       if(nchan > 1) {
+               xfer_mode |= CMD_MODE_STEREO;
+       }
+
+       if(!(size = audio_callback(buffer, 65536))) {
+               return;
+       }
+
+       intr.pm_offset = (intptr_t)intr_handler;
+       intr.pm_selector = _go32_my_cs();
+       _disable();
+       _go32_dpmi_set_protected_mode_interrupt_vector(IRQ_TO_INTR(irq), &intr);
+       _enable();
+
+       sb_set_output_rate(rate);
+       start_dma_transfer(addr, size);
+       write_dsp(CMD_ENABLE_OUTPUT);
+}
+
+void sb_pause(void)
+{
+       write_dsp(CMD_PAUSE_DMA8);
+}
+
+void sb_continue(void)
+{
+       write_dsp(CMD_CONT_DMA8);
+}
+
+void sb_stop(void)
+{
+       write_dsp(CMD_END_DMA8);
+       write_dsp(CMD_DISABLE_OUTPUT);
+}
+
+void sb_volume(int vol)
+{
+       /* TODO */
+}
+
+static void intr_handler()
+{
+       int size;
+
+       /* ask for more data */
+       if(!(size = audio_callback(buffer, 65536))) {
+               sb_stop();
+               return;
+       }
+       start_dma_transfer((uint32_t)buffer, size);
+
+       /* acknowledge the interrupt */
+       inportb(REG_INTACK);
+
+       if(irq > 7) {
+               outportb(PIC2_CMD, OCW2_EOI);
+       }
+       outportb(PIC1_CMD, OCW2_EOI);
+}
+
+static void start_dma_transfer(uint32_t addr, int size)
+{
+       /* set up the next DMA transfer */
+       dma_out(dma_chan, addr, size, DMA_SINGLE);
+
+       /* program the DSP to accept the DMA transfer */
+       write_dsp(CMD_START_DMA8 | CMD_FIFO);
+       write_dsp(xfer_mode);
+       size--;
+       write_dsp(size & 0xff);
+       write_dsp((size >> 8) & 0xff);
+}
+
+static void write_dsp(unsigned char val)
+{
+       while(inportb(REG_WSTAT) & WSTAT_BUSY);
+       outportb(REG_WDATA, val);
+}
+
+static unsigned char read_dsp(void)
+{
+       while((inportb(REG_RSTAT) & RSTAT_RDY) == 0);
+       return inportb(REG_RDATA);
+}
+
+static void write_mix(unsigned char val, int reg)
+{
+       outportb(REG_MIXPORT, reg);
+       outportb(REG_MIXDATA, val);
+}
+
+static unsigned char read_mix(int reg)
+{
+       outportb(REG_MIXPORT, reg);
+       return inportb(REG_MIXDATA);
+}
+
+static int get_dsp_version(void)
+{
+       int major, minor;
+
+       write_dsp(CMD_GET_VER);
+       major = read_dsp();
+       minor = read_dsp();
+
+       return (major << 8) | minor;
+}
+
+static int sb16_detect_irq(void)
+{
+       int i, irqsel;
+       static int irqtab[] = {2, 5, 7, 10};
+
+       irq = 0;
+       irqsel = read_mix(MIX_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_IRQ_SEL);      /* bit1 selects irq 5 */
+
+               /* re-read to verify */
+               irqsel = read_mix(MIX_IRQ_SEL);
+               if(irqsel != 2) {
+                       return -1;
+               }
+               irq = 5;
+       }
+
+       return irq;
+}
+
+static int sb16_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_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_DMA_SEL);
+
+               /* re-read to verify */
+               tmp = read_mix(MIX_DMA_SEL);
+               if(tmp != dmasel) {
+                       return -1;
+               }
+               dma_chan = 1;
+               dma16_chan = 5;
+       }
+
+       return dma_chan;
+}
+
+#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 (file)
index 0000000..d437ff7
--- /dev/null
@@ -0,0 +1,42 @@
+/*
+pcboot - bootable PC demo/game kernel
+Copyright (C) 2018  John Tsiombikas <nuclear@member.fsf.org>
+
+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 3 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, see <https://www.gnu.org/licenses/>.
+*/
+#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 nchan);
+void sb_pause(void);
+void sb_continue(void);
+void sb_stop(void);
+
+void sb_volume(int vol);
+
+#endif /* AU_SB_H_ */
diff --git a/src/audio.c b/src/audio.c
new file mode 100644 (file)
index 0000000..49b8a8d
--- /dev/null
@@ -0,0 +1,88 @@
+/*
+pcboot - bootable PC demo/game kernel
+Copyright (C) 2018  John Tsiombikas <nuclear@member.fsf.org>
+
+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 3 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, see <https://www.gnu.org/licenses/>.
+*/
+#include <stdio.h>
+#include "audio.h"
+#include "au_sb.h"
+
+struct audrv {
+       void *(*get_buffer)(int *size);
+       void (*start)(int rate, int nchan);
+       void (*pause)(void);
+       void (*cont)(void);
+       void (*stop)(void);
+       void (*volume)(int vol);
+};
+
+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;
+               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 nchan)
+{
+       drv.start(rate, 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);
+}
diff --git a/src/audio.h b/src/audio.h
new file mode 100644 (file)
index 0000000..9b528c2
--- /dev/null
@@ -0,0 +1,35 @@
+/*
+pcboot - bootable PC demo/game kernel
+Copyright (C) 2018  John Tsiombikas <nuclear@member.fsf.org>
+
+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 3 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, see <https://www.gnu.org/licenses/>.
+*/
+#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 nchan);
+void audio_pause(void);
+void audio_resume(void);
+void audio_stop(void);
+
+void audio_volume(int vol);
+
+#endif /* AUDIO_H_ */
diff --git a/src/dma.c b/src/dma.c
new file mode 100644 (file)
index 0000000..9c6ad67
--- /dev/null
+++ b/src/dma.c
@@ -0,0 +1,132 @@
+#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);
+       outportb(DMA_CLR_FLIPFLOP(chan), 0);
+
+       /* single / block / cascade */
+       mode = ((flags & 3) << 6) | MODE_CHAN(chan);
+       if(flags & DMA_DECR) mode |= MODE_DECR;
+       if(flags & DMA_AUTO) mode |= MODE_AUTO;
+       outportb(DMA_MODE(chan), mode);
+
+       if(IS_16BIT(chan)) {
+               phyaddr >>= 1;
+               size >>= 1;
+       }
+
+       outportb(addr_port, phyaddr & 0xff);
+       outportb(addr_port, (phyaddr >> 8) & 0xff);
+       outportb(page_port[chan], (phyaddr >> 16) & 0xff);
+
+       size--;
+       outportb(count_port, size & 0xff);
+       outportb(count_port, (size >> 8) & 0xff);
+
+       unmask(chan);
+}
+
+static inline void mask(int chan)
+{
+       outportb(DMA_MASK(chan), MASK_CHAN(chan) | MASK_DISABLE);
+}
+
+static inline void unmask(int chan)
+{
+       outportb(DMA_MASK(chan), MASK_CHAN(chan));
+}
diff --git a/src/dma.h b/src/dma.h
new file mode 100644 (file)
index 0000000..ca12ad1
--- /dev/null
+++ b/src/dma.h
@@ -0,0 +1,17 @@
+#ifndef DMA_H_
+#define DMA_H_
+
+#include <inttypes.h>
+
+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/main.c b/src/main.c
new file mode 100644 (file)
index 0000000..44f41b8
--- /dev/null
@@ -0,0 +1,43 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include "audio.h"
+
+static int au_callback(void *buffer, int size, void *cls);
+
+/* defined in sndsamples.s */
+extern signed char snd_click[];
+extern int snd_click_size;
+
+int main(int argc, char **argv)
+{
+       audio_init();
+       audio_set_callback(au_callback, 0);
+
+       audio_play(22050, 1);
+
+       return 0;
+}
+
+/* snd_click_size is < 65536 so we can just throw it all at once in there */
+static int au_callback(void *buffer, int size, void *cls)
+{
+       int i;
+       signed char *ptr = buffer;
+       signed char *src = snd_click;
+       /*
+          for(i=0; i<size; i++) {
+          if((i / 32) & 1) {
+        *ptr++ = -64;
+        } else {
+        *ptr++ = 64;
+        }
+        }
+        */
+       /*memcpy(buffer, snd_click, snd_click_size);*/
+
+       for(i=0; i<snd_click_size; i++) {
+               *ptr++ = *src++;
+       }
+
+       return snd_click_size;
+}