2 pcboot - bootable PC demo/game kernel
3 Copyright (C) 2018 John Tsiombikas <nuclear@member.fsf.org>
5 This program is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation, either version 3 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY, without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program. If not, see <https://www.gnu.org/licenses/>.
25 #define REG_MIXPORT (base_port + 0x4)
26 #define REG_MIXDATA (base_port + 0x5)
27 #define REG_RESET (base_port + 0x6)
28 #define REG_RDATA (base_port + 0xa)
29 #define REG_WDATA (base_port + 0xc)
30 #define REG_WSTAT (base_port + 0xc)
31 #define REG_RSTAT (base_port + 0xe)
32 #define REG_INTACK (base_port + 0xe)
33 #define REG_INT16ACK (base_port + 0xf)
35 #define WSTAT_BUSY 0x80
36 #define RSTAT_RDY 0x80
39 #define CMD_SB16_OUT_RATE 0x41
40 #define CMD_SB16_IN_RATE 0x42
41 #define CMD_GET_VER 0xe1
43 /* start DMA playback/recording. combine with fifo/auto/input flags */
44 #define CMD_START_DMA8 0xc0
45 #define CMD_START_DMA16 0xb0
48 #define CMD_INPUT 0x08
50 /* immediately pause/continue */
51 #define CMD_PAUSE_DMA8 0xd0
52 #define CMD_CONT_DMA8 0xd4
53 #define CMD_PAUSE_DMA16 0xd5
54 #define CMD_CONT_DMA16 0xd6
56 /* end the playback at the end of the current buffer */
57 #define CMD_END_DMA16 0xd9
58 #define CMD_END_DMA8 0xda
60 /* transfer mode commands */
61 #define CMD_MODE_SIGNED 0x10
62 #define CMD_MODE_STEREO 0x20
64 #define VER_MAJOR(x) ((x) >> 8)
65 #define VER_MINOR(x) ((x) & 0xff)
67 static void intr_handler();
68 static void start_dma_transfer(uint32_t addr, int size);
69 static void write_dsp(unsigned char val);
70 static unsigned char read_dsp(void);
71 static int get_dsp_version(void);
72 static const char *sbname(int ver);
74 extern unsigned char low_mem_buffer[];
77 static int irq, dma_chan;
87 base_port = 0x200 + ((i + 1) << 4);
88 if(sb_reset_dsp() == 0) {
89 ver = get_dsp_version();
90 sb16 = VER_MAJOR(ver) >= 4;
91 printf("sb_detect: found %s (DSP v%d.%02d) at port %xh\n", sbname(ver),
92 VER_MAJOR(ver), VER_MINOR(ver), base_port);
97 /* TODO: detect these somehow? */
104 int sb_reset_dsp(void)
109 for(i=0; i<3; i++) iodelay();
112 for(i=0; i<128; i++) {
113 if(inb(REG_RSTAT) & RSTAT_RDY) {
114 if(inb(REG_RDATA) == 0xaa) {
123 void sb_set_output_rate(int rate)
126 write_dsp(CMD_SB16_OUT_RATE);
127 write_dsp(rate >> 8);
128 write_dsp(rate & 0xff);
130 int tcon = 256 - 1000000 / rate;
136 void *sb_buffer(int *size)
142 void sb_start(int rate, int nchan)
147 /* for now just use the are after boot2. it's only used by the second stage
148 * loader and the VBE init code, none of which should overlap with audio playback.
149 * It's not necessary to use low memory. We can use up to the first 16mb for this,
150 * so if this becomes an issue, I'll make a DMA buffer allocator over 1mb.
151 * start the buffer from the next 64k boundary.
153 addr = ((uint32_t)low_mem_buffer + 0xffff) & 0xffff0000;
154 buffer = (void*)addr;
156 xfer_mode = CMD_MODE_SIGNED;
158 xfer_mode |= CMD_MODE_STEREO;
161 if(!(size = audio_callback(buffer, 65536))) {
165 interrupt(IRQ_TO_INTR(irq), intr_handler);
167 start_dma_transfer(addr, size);
172 write_dsp(CMD_PAUSE_DMA8);
175 void sb_continue(void)
177 write_dsp(CMD_CONT_DMA8);
182 write_dsp(CMD_END_DMA8);
185 void sb_volume(int vol)
190 static void intr_handler()
194 /* ask for more data */
195 if(!(size = audio_callback(buffer, 65536))) {
199 start_dma_transfer((uint32_t)buffer, size);
201 /* acknowledge the interrupt */
205 static void start_dma_transfer(uint32_t addr, int size)
207 /* set up the next DMA transfer */
208 dma_out(dma_chan, addr, size, DMA_SINGLE);
210 /* program the DSP to accept the DMA transfer */
211 write_dsp(CMD_START_DMA8 | CMD_FIFO);
212 write_dsp(xfer_mode);
214 write_dsp(size & 0xff);
215 write_dsp((size >> 8) & 0xff);
218 static void write_dsp(unsigned char val)
220 while(inb(REG_WSTAT) & WSTAT_BUSY);
221 outb(val, REG_WDATA);
224 static unsigned char read_dsp(void)
226 while((inb(REG_RSTAT) & RSTAT_RDY) == 0);
227 return inb(REG_RDATA);
230 static int get_dsp_version(void)
234 write_dsp(CMD_GET_VER);
238 return (major << 8) | minor;
241 #define V(maj, min) (((maj) << 8) | (min))
243 static const char *sbname(int ver)
245 int major = VER_MAJOR(ver);
246 int minor = VER_MINOR(ver);
251 return "Sound Blaster 1.5";
253 return "Sound Blaster 1.0";
256 if(minor == 1 || minor == 2) {
257 return "Sound Blaster 2.0";
264 return "Sound Blaster Pro";
267 return "Sound Blaster Pro 2";
269 return "Gallant SC-6000";
279 return "Sound Blaster 16";
281 return "Sound Blaster 16 SCSI-2";
283 return "Sound Blaster AWE 32";
285 return "Sound Blaster ViBRA16C";
287 return "Sound Blaster AWE 64";
294 return "Unknown Sound Blaster";