Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[MS-DOS] Preliminary audio support using PC-Speaker #128

Draft
wants to merge 3 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 5 additions & 3 deletions platforms/msdos/Makefile
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
CC := ia16-elf-gcc
SOURCES := extras.c video.c input.c ../../common/main.c ../../common/minefield.c ../../common/video-tiles.c

# extras.c implements _start and must be the first one here.
SOURCES := extras.c audio.c video.c input.c ../../common/main.c ../../common/minefield.c ../../common/video-tiles.c
BUILDDIR := build
CFLAGS := -nodefaultlibs -nostdlib -mcmodel=tiny -mregparmcall -march=i186 -Os -flto -Wall -pedantic -I../../common -I. -DNO_UNUSED_MACRO
LDFLAGS := -flto
CFLAGS := -nodefaultlibs -nostdlib -mcmodel=tiny -mregparmcall -march=i186 -Os -Wall -I../../common -I. -DNO_UNUSED_MACRO
LDFLAGS :=
SCREEN_RESOLUTION := -DSCREEN_WIDTH=320/8 -DSCREEN_HEIGHT=200/8 -DMINEFIELD_X_OFFSET=10 -DMINEFIELD_Y_OFFSET=2

PYTHON := python3
Expand Down
102 changes: 102 additions & 0 deletions platforms/msdos/audio.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
#include <stdint.h>
#include <stdlib.h>

static union {
uint32_t dword;
struct {
uint16_t low_word, high_word;
};
} orig_bios_irq0_isr;
static uint16_t sample_rate_counter;

static inline void outb(uint16_t port, uint8_t value)
{
asm __volatile("outb %0, %1" : : "Ral"(value), "Nd"(port));
}

static inline uint8_t inb(uint16_t port)
{
uint8_t value;
asm __volatile("inb %1, %0" : "=Ral"(value) : "Nd"(port));
return value;
}

static inline void audio_set_sample_rate(const uint16_t sample_rate)
{
uint32_t corrected = sample_rate ? (0x1234dc / (uint32_t)sample_rate) : 0;

sample_rate_counter = corrected & 0xfffe;

asm __volatile("cli");
outb(0x43, 0x36);
outb(0x40, sample_rate_counter & 0xff);
outb(0x40, sample_rate_counter >> 8);
asm __volatile("sti");
}

static inline void audio_play_sample(uint8_t sample)
{
outb(0x43, 0xb0);
outb(0x42, sample >> 1);
outb(0x42, 0);
}

__attribute__((used)) static void audio_tick(void)
{
static uint16_t tick;

audio_play_sample(rand() & 255);

tick += sample_rate_counter;

if (sample_rate_counter > tick) {
asm __volatile("pushf");
asm __volatile("lcall *(%0)" : : "b"(&orig_bios_irq0_isr));
} else {
outb(0x20, 0x20); /* acknowledge IRQ */
}
}

void audio_tick_isr(void);
__asm__(".global audio_tick_isr\n"
"audio_tick_isr:\n"
"pusha\n"
"call audio_tick\n"
"popa\n"
"iret\n");

void audio_init(void)
{
/* Save original BIOS IRQ0 handler */
asm __volatile("int $0x21\n"
: "=b"(orig_bios_irq0_isr.low_word),
"=e"(orig_bios_irq0_isr.high_word)
: "a"(0x3508)
: "cc", "memory");

/* Replace it with ours */
asm __volatile("int $0x21"
:
: "d"(audio_tick_isr), "a"(0x2508)
: "cc", "memory");

/* Connect speaker to PIT channel 2 */
outb(0x61, inb(0x61) | 0x3);

/* Reprogram PIT Channel 0 to fire IRQ0 at 16KHz */
audio_set_sample_rate(16000);
}

void audio_shutdown(void)
{
audio_set_sample_rate(0);

/* Restore original BIOS IRQ0 routine */
asm __volatile("int $0x21\n"
:
: "Rds"(orig_bios_irq0_isr.high_word),
"d"(orig_bios_irq0_isr.low_word), "a"(0x2508)
: "cc", "memory");

outb(0x61, inb(0x61) & 0xfc);
}
14 changes: 10 additions & 4 deletions platforms/msdos/extras.c
Original file line number Diff line number Diff line change
Expand Up @@ -36,15 +36,21 @@ time_t time(time_t *t)

void *malloc(size_t v)
{
static char heap[4096];
static char heap[5000];
static char *heap_ptr = heap;
char *old_heap_ptr;

old_heap_ptr = heap_ptr;
heap_ptr += v;
if (sizeof(heap) - (heap_ptr - heap) < v) {
asm __volatile("int $16\n"
"jmp exit"
:
: "a"((uint16_t)0));

if (heap_ptr > heap + sizeof(heap))
return NULL;
}

old_heap_ptr = heap_ptr;
heap_ptr += v;

return old_heap_ptr;
}
Expand Down
17 changes: 12 additions & 5 deletions platforms/msdos/video.c
Original file line number Diff line number Diff line change
Expand Up @@ -207,7 +207,7 @@ static inline void set_tile_full(uint8_t dst_x, uint8_t dst_y, uint8_t tile, int
{
static uint8_t __far *video_seg = (uint8_t __far *)0xa0000000;
uint8_t __far *video_seg_start =
&video_seg[320 * (int)dst_y * 8 + (int)dst_x * 8];
&video_seg[320 * (unsigned int)dst_y * 8 + (unsigned int)dst_x * 8];
const uint16_t offs = get_tile_offset(tile);
const uint16_t off_high = 8 * (offs >> 4);
const uint16_t off_low = 8 * (offs & 0xf);
Expand Down Expand Up @@ -261,18 +261,16 @@ void highlight_current_cell(minefield *mf)
old_y = y;
}


#ifdef USE_DEBUG_MODE
/* Thanks to OSDev Wiki for the serial initialization sequence! */

#define SERIAL_PORT_ADDR 0x3f8
static inline uint8_t inb(uint16_t port)
{
uint8_t value;
asm __volatile("inb %1, %0" : "=Ral"(value) : "Nd"(port));
return value;
}

/* Thanks to OSDev Wiki for the serial initialization sequence! */
#define SERIAL_PORT_ADDR 0x3f8

static void serial_debug_init(void)
{
Expand Down Expand Up @@ -336,6 +334,7 @@ void debug_mode(uint8_t mode)
#endif



void platform_init()
{
#ifdef USE_DEBUG_MODE
Expand All @@ -346,6 +345,9 @@ void platform_init()
srand(time(NULL));

video_init();

void audio_init(void);
audio_init();
}


Expand All @@ -356,6 +358,11 @@ void platform_shutdown()
{
free(mines_xpm[0]);
free(mines_xpm);

set_mode(VGA_TEXT_MODE);

void audio_shutdown(void);
audio_shutdown();

exit(0);
}