Skip to content

Commit 78676f6

Browse files
committed
[MS-DOS] Preliminary audio support using PC-Speaker
This uses the PIT to control the PC speaker frequency, and play a random sample in the ISR. For some reason, this works fine as long as we don't get into graphical mode. I don't yet know why.
1 parent 5a7aa40 commit 78676f6

File tree

2 files changed

+93
-6
lines changed

2 files changed

+93
-6
lines changed

platforms/msdos/Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
CC := ia16-elf-gcc
22
SOURCES := extras.c video.c input.c ../../common/main.c ../../common/minefield.c ../../common/video-tiles.c
33
BUILDDIR := build
4-
CFLAGS := -nodefaultlibs -nostdlib -mcmodel=tiny -mregparmcall -march=i186 -Os -flto -Wall -pedantic -I../../common -I. -DNO_UNUSED_MACRO
4+
CFLAGS := -nodefaultlibs -nostdlib -mcmodel=tiny -mregparmcall -march=i186 -Os -flto -Wall -I../../common -I. -DNO_UNUSED_MACRO
55
LDFLAGS := -flto
66
SCREEN_RESOLUTION := -DSCREEN_WIDTH=320/8 -DSCREEN_HEIGHT=200/8 -DMINEFIELD_X_OFFSET=10 -DMINEFIELD_Y_OFFSET=2
77

platforms/msdos/video.c

Lines changed: 92 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -260,18 +260,16 @@ void highlight_current_cell(minefield *mf)
260260
old_y = y;
261261
}
262262

263-
264-
#ifdef USE_DEBUG_MODE
265-
/* Thanks to OSDev Wiki for the serial initialization sequence! */
266-
267-
#define SERIAL_PORT_ADDR 0x3f8
268263
static inline uint8_t inb(uint16_t port)
269264
{
270265
uint8_t value;
271266
asm __volatile("inb %1, %0" : "=Ral"(value) : "Nd"(port));
272267
return value;
273268
}
274269

270+
#ifdef USE_DEBUG_MODE
271+
/* Thanks to OSDev Wiki for the serial initialization sequence! */
272+
#define SERIAL_PORT_ADDR 0x3f8
275273

276274
static void serial_debug_init(void)
277275
{
@@ -334,6 +332,92 @@ void debug_mode(uint8_t mode)
334332
}
335333
#endif
336334

335+
static union {
336+
uint32_t dword;
337+
struct {
338+
uint16_t low_word, high_word;
339+
};
340+
} orig_bios_irq0_isr;
341+
static uint16_t sample_rate_counter;
342+
343+
static inline void audio_set_sample_rate(const uint16_t sample_rate)
344+
{
345+
uint32_t corrected = sample_rate ? (0x1234dc / (uint32_t)sample_rate) : 0;
346+
347+
sample_rate_counter = corrected & 0xfffe;
348+
349+
asm __volatile("cli");
350+
outb(0x43, 0x36);
351+
outb(0x40, sample_rate_counter & 0xff);
352+
outb(0x40, sample_rate_counter >> 8);
353+
asm __volatile("sti");
354+
}
355+
356+
static inline void audio_play_sample(uint8_t sample)
357+
{
358+
outb(0x43, 0xb0);
359+
outb(0x42, sample);
360+
outb(0x42, 0);
361+
}
362+
363+
__attribute__((used)) static void audio_tick(void)
364+
{
365+
static uint16_t tick;
366+
uint8_t sample = rand() & 255;
367+
368+
outb(0x43, 0xb0);
369+
outb(0x42, sample >> 1);
370+
outb(0x42, 0);
371+
372+
uint32_t v = (uint32_t)tick + sample_rate_counter;
373+
tick = (uint16_t)v;
374+
375+
if ((uint32_t)tick != v) {
376+
asm __volatile("pushf");
377+
void (*far_void_func)(void) __far =
378+
(void (*)(void) __far)orig_bios_irq0_isr.dword;
379+
far_void_func();
380+
} else {
381+
outb(0x20, 0x20); /* acknowledge IRQ */
382+
}
383+
}
384+
385+
extern void audio_tick_isr(void);
386+
__asm__(".global audio_tick_isr\n"
387+
"audio_tick_isr:\n"
388+
"call audio_tick\n"
389+
"iret\n");
390+
391+
static inline void audio_init(void)
392+
{
393+
/* Save original BIOS IRQ0 handler */
394+
asm __volatile("int $0x21\n"
395+
: "=b"(orig_bios_irq0_isr.low_word),
396+
"=e"(orig_bios_irq0_isr.high_word)
397+
: "a"(0x3508));
398+
399+
/* Replace it with ours */
400+
asm __volatile("int $0x21" : : "d"(audio_tick_isr), "a"(0x2508));
401+
402+
/* Connect speaker to PIT channel 2 */
403+
outb(0x61, inb(0x61) | 0x3);
404+
405+
/* Reprogram PIT Channel 0 to fire IRQ0 at 16KHz */
406+
audio_set_sample_rate(16000);
407+
}
408+
409+
static inline void audio_shutdown(void)
410+
{
411+
audio_set_sample_rate(0);
412+
413+
/* Restore original BIOS IRQ0 routine */
414+
asm __volatile("int $0x21\n"
415+
:
416+
: "Rds"(orig_bios_irq0_isr.low_word),
417+
"d"(orig_bios_irq0_isr.high_word), "a"(0x2508));
418+
419+
outb(0x61, inb(0x61) & 0xfc);
420+
}
337421

338422
void platform_init()
339423
{
@@ -345,6 +429,8 @@ void platform_init()
345429
srand(time(NULL));
346430

347431
video_init();
432+
audio_init();
433+
348434
draw_scenario();
349435
}
350436

@@ -357,5 +443,6 @@ void platform_shutdown()
357443
free(mines_xpm[0]);
358444
free(mines_xpm);
359445
set_mode(VGA_TEXT_MODE);
446+
audio_shutdown();
360447
exit(0);
361448
}

0 commit comments

Comments
 (0)