*.raw filter=lfs diff=lfs merge=lfs -text
*.pal filter=lfs diff=lfs merge=lfs -text
+*.pcm filter=lfs diff=lfs merge=lfs -text
csrc = $(wildcard src/*.c) $(wildcard src/libc/*.c) $(wildcard src/test/*.c)
-ssrc = $(wildcard src/*.s) $(wildcard src/libc/*.s) $(wildcard src/boot/*.s)
+ssrc = $(wildcard src/*.s) $(wildcard src/libc/*.s) $(wildcard src/boot/*.s) $(wildcard src/test/*.s)
Ssrc = $(wildcard src/*.S)
obj = $(csrc:.c=.o) $(ssrc:.s=.o) $(Ssrc:.S=.o)
dep = $(obj:.o=.d)
#include "audio.h"
#include "au_sb.h"
#include "asmops.h"
+#include "intr.h"
+#include "dma.h"
#define REG_MIXPORT (base_port + 0x4)
#define REG_MIXDATA (base_port + 0x5)
#define WSTAT_BUSY 0x80
#define RSTAT_RDY 0x80
-#define CMD_SET_RATE 0x41
-#define CMD_PLAY_PCM 0xa6
-#define CMD_STOP_PCM 0xd9
-#define CMD_GET_VER 0xe1
+#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_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
#define VER_MAJOR(x) ((x) >> 8)
#define VER_MINOR(x) ((x) & 0xff)
+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 int get_dsp_version(void);
static const char *sbname(int ver);
+extern unsigned char low_mem_buffer[];
+
static int base_port;
+static int irq, dma_chan;
+static int sb16;
+static void *buffer;
+static int xfer_mode;
int sb_detect(void)
{
base_port = 0x200 + ((i + 1) << 4);
if(sb_reset_dsp() == 0) {
ver = get_dsp_version();
+ sb16 = VER_MAJOR(ver) >= 4;
printf("sb_detect: found %s (DSP v%d.%02d) at port %xh\n", sbname(ver),
VER_MAJOR(ver), VER_MINOR(ver), base_port);
return 1;
}
}
+ /* TODO: detect these somehow? */
+ irq = 5;
+ dma_chan = 1;
+
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;
+
+ /* 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;
+ }
+
+ interrupt(IRQ_TO_INTR(irq), intr_handler);
+
+ start_dma_transfer(addr, size);
+}
+
+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);
+}
+
+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 */
+ inb(REG_INTACK);
+}
+
+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(inb(REG_WSTAT) & WSTAT_BUSY);
*/
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_ */
#include "audio.h"
#include "au_sb.h"
-void init_audio(void)
+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()) {
- /* TODO use the sound blaster */
+ 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);
+}
#ifndef AUDIO_H_
#define AUDIO_H_
-void init_audio(void);
+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_ */
--- /dev/null
+/*
+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 "dma.h"
+#include "asmops.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);
+ outb(0, DMA_CLR_FLIPFLOP(chan));
+
+ /* single / block / cascade */
+ mode = ((flags & 3) << 6) | MODE_CHAN(chan);
+ if(flags & DMA_DECR) mode |= MODE_DECR;
+ if(flags & DMA_AUTO) mode |= MODE_AUTO;
+ outb(mode, DMA_MODE(chan));
+
+ if(IS_16BIT(chan)) {
+ phyaddr >>= 1;
+ size >>= 1;
+ }
+
+ outb(phyaddr & 0xff, addr_port);
+ outb((phyaddr >> 8) & 0xff, addr_port);
+ outb((phyaddr >> 16) & 0xff, page_port[chan]);
+
+ size--;
+ outb(size & 0xff, count_port);
+ outb((size >> 8) & 0xff, count_port);
+
+ unmask(chan);
+}
+
+static inline void mask(int chan)
+{
+ outb(MASK_CHAN(chan) | MASK_DISABLE, DMA_MASK(chan));
+}
+
+static inline void unmask(int chan)
+{
+ outb(MASK_CHAN(chan), DMA_MASK(chan));
+}
--- /dev/null
+/*
+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 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_ */
/* initialize the timer */
init_timer();
- init_audio();
+ audio_init();
enable_intr();
--- /dev/null
+ .data
+ .global snd_click
+ .global snd_click_size
+snd_click:
+ .incbin "click-s8_mono.pcm"
+snd_click_size: .long . - snd_click
#include "keyb.h"
#include "psaux.h"
#include "contty.h"
+#include "audio.h"
static void draw_cursor(int x, int y, uint16_t col);
+static int click_sound_callback(void *buffer, int size, void *cls);
static uint16_t *framebuf;
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xffff, 0xffff, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000
};
+static int click;
+
+/* defined in sndsamples.s */
+extern void *snd_click;
+extern int snd_click_size;
+
int vbetest(void)
{
int i, j, nmodes, mx, my;
- unsigned int st;
+ unsigned int st, prev_st = 0;
struct video_mode vi;
uint16_t *fbptr;
set_mouse_bounds(0, 0, 639, 479);
+ audio_set_callback(click_sound_callback, 0);
+
/* empty the kb queue */
while(kb_getkey() != -1);
}
st = mouse_state(&mx, &my);
+
+ for(i=0; i<3; i++) {
+ unsigned int bit = 1 << i;
+ if(((st & bit) ^ (prev_st & bit)) & (st & bit)) {
+ click = 1;
+ }
+ }
+ if(click) {
+ printf("click!\n");
+ audio_play(22050, 1);
+ }
+
+ prev_st = st;
+
draw_cursor(mx, my, st & 1 ? 0xf800 : (st & 2 ? 0x7e0 : (st & 4 ? 0x00ff : 0)));
halt_cpu();
savp += CURSOR_XSZ - w;
}
}
+
+/* snd_click_size is < 65536 so we can just throw it all at once in there */
+static int click_sound_callback(void *buffer, int size, void *cls)
+{
+ if(click) {
+ memcpy(buffer, snd_click, snd_click_size);
+ click = 0;
+ return snd_click_size;
+ }
+ return 0;
+}