diff --git a/Makefile b/Makefile index 1a1b522..fdac036 100644 --- a/Makefile +++ b/Makefile @@ -44,7 +44,9 @@ OBJECTS = ./kernel/kstart.o ./kernel.o ./console.o ./drivers/vga.o ./drivers/uar ./fs/fs.o ./drivers/ata.o ./lib/string.o ./proc.o ./drivers/pit.o ./kernel/vm.o run: image.bin - qemu-system-i386 -drive format=raw,file=$< -serial mon:stdio -qmp unix:qemu-monitor-socket,server,nowait + qemu-system-i386 -drive format=raw,file=$< -serial mon:stdio -qmp unix:qemu-monitor-socket,server,nowait \ + -audiodev pa,id=SAUND \ + -machine pcspk-audiodev=SAUND run-nox: image.bin qemu-system-i386 -nographic -drive format=raw,file=$< -serial mon:stdio -qmp unix:qemu-monitor-socket,server,nowait @@ -106,7 +108,7 @@ debug-nox: image.bin -ex "break _start" \ -ex "continue" -USERPROGS=./user/false ./user/greet ./user/div0 ./user/shout ./user/badputs ./user/bss ./user/snake +USERPROGS=./user/false ./user/greet ./user/div0 ./user/shout ./user/badputs ./user/bss ./user/snake ./user/player fs.img: ./kernel.bin ./tools/mkfs $(USERPROGS) ./tools/mkfs $@ $< $(USERPROGS) diff --git a/cpu/idt.c b/cpu/idt.c index 291986d..fd99d4b 100644 --- a/cpu/idt.c +++ b/cpu/idt.c @@ -299,6 +299,13 @@ static void handle_syscall(registers_t* r) { case SYS_getc: r->eax = handle_getc(); break; + case SYS_set_beep: + if (r->ebx > MAX_BEEP_FREQUENCY_HZ) { + userspace_panic("Userspace panic: beep frequency out of range\n"); + } + set_beep_frequency_hz(r->ebx); + r->eax = 0; + break; default: userspace_panic("Userspace panic: Unknown syscall\n"); } diff --git a/drivers/pit.c b/drivers/pit.c index f964575..8d77ce3 100644 --- a/drivers/pit.c +++ b/drivers/pit.c @@ -23,6 +23,11 @@ enum { PIT_MODES_SQUARE_WAVE_GENERATOR = 0x3, PIT_MODES_SW_TRIGGERRED_STROBE = 0x4, PIT_MODES_HW_TRIGGERRED_STROBE = 0x5, + + PIT_COMMAND_PORT = 0x43, + PIT_CHANNEL0_DATA_PORT = 0x40, + PIT_CHANNEL2_DATA_PORT = 0x42, + PC_SPEAKER_PORT = 0x61, }; @@ -30,6 +35,7 @@ enum { static timer_callback callbacks[100]; static int registered_callbacks = 0; static volatile uint32_t uptime_ms = 0; +static volatile uint32_t speaker_frequency_hz = 0; void add_timer_callback(timer_callback tc) { callbacks[registered_callbacks++] = tc; @@ -57,9 +63,9 @@ void init_pit() { .operating_mode = PIT_MODES_SQUARE_WAVE_GENERATOR, .bcd = 0, }; - port_byte_out(0x43, *(unsigned char *)(&cmd)); - port_byte_out(0x40, PIT_PROGRAM_REG & 0xff); - port_byte_out(0x40, (PIT_PROGRAM_REG & 0xff00) >> 8); + port_byte_out(PIT_COMMAND_PORT, *(unsigned char *)(&cmd)); + port_byte_out(PIT_CHANNEL0_DATA_PORT, PIT_PROGRAM_REG & 0xff); + port_byte_out(PIT_CHANNEL0_DATA_PORT, (PIT_PROGRAM_REG & 0xff00) >> 8); register_interrupt_handler(IRQ0, timer_interrupt_handler); add_timer_callback(timer_int_callback); @@ -76,6 +82,45 @@ uint32_t get_uptime_ms(void) { return uptime_ms; } +void set_beep_frequency_hz(uint32_t frequency_hz) { + if (frequency_hz == 0) { + disable_beep(); + return; + } + + uint32_t divisor = PIT_CRYSTAL_HZ / frequency_hz; + if (divisor == 0) { + divisor = 1; + } + if (divisor > 0xffff) { + divisor = 0xffff; + } + + struct pit_command_t cmd = { + .select_channel = PIT_SELECT_CHANNEL2, + .access_mode = PIT_ACCESS_MODE_LOHIBYTE, + .operating_mode = PIT_MODES_SQUARE_WAVE_GENERATOR, + .bcd = 0, + }; + port_byte_out(PIT_COMMAND_PORT, *(unsigned char *)(&cmd)); + port_byte_out(PIT_CHANNEL2_DATA_PORT, divisor & 0xff); + port_byte_out(PIT_CHANNEL2_DATA_PORT, (divisor & 0xff00) >> 8); + + uint8_t speaker = port_byte_in(PC_SPEAKER_PORT); + port_byte_out(PC_SPEAKER_PORT, speaker | 0x03); + speaker_frequency_hz = frequency_hz; +} + +void disable_beep(void) { + uint8_t speaker = port_byte_in(PC_SPEAKER_PORT); + port_byte_out(PC_SPEAKER_PORT, speaker & 0xfc); + speaker_frequency_hz = 0; +} + +uint32_t get_beep_frequency_hz(void) { + return speaker_frequency_hz; +} + void msleep(int ms) { sleep_counter = ms / 10; while (sleep_counter > 0) { diff --git a/drivers/pit.h b/drivers/pit.h index 0778e7b..7167b94 100644 --- a/drivers/pit.h +++ b/drivers/pit.h @@ -2,10 +2,15 @@ #include +#define MAX_BEEP_FREQUENCY_HZ 44100u + typedef void (*timer_callback)(void); void init_pit(void); void add_timer_callback(timer_callback tc); uint32_t get_uptime_ms(void); +void set_beep_frequency_hz(uint32_t frequency_hz); +void disable_beep(void); +uint32_t get_beep_frequency_hz(void); void msleep(int ms); diff --git a/proc.c b/proc.c index 64e8602..66d6850 100644 --- a/proc.c +++ b/proc.c @@ -1,4 +1,5 @@ #include "proc.h" +#include "drivers/pit.h" #include "drivers/vga.h" struct context { @@ -88,6 +89,7 @@ void run_elf(const char* name) { _Noreturn void killproc() { void* task_stack; + disable_beep(); if (!vga_is_text_mode()) { switch_to_text_mode(); vga_clear_screen(); diff --git a/syscall.h b/syscall.h index ea60dc2..39821be 100644 --- a/syscall.h +++ b/syscall.h @@ -14,6 +14,7 @@ enum { SYS_time_ms = 7, SYS_halt = 8, SYS_getc = 9, + SYS_set_beep = 10, }; int syscall(int call, uintptr_t arg); diff --git a/user/player.c b/user/player.c new file mode 100644 index 0000000..73a5d80 --- /dev/null +++ b/user/player.c @@ -0,0 +1,24 @@ +#include "../syscall.h" +#include + +static uint32_t time_ms(void) { + return (uint32_t)syscall(SYS_time_ms, 0); +} + +static void wait_ms(uint32_t duration_ms) { + uint32_t start = time_ms(); + while ((uint32_t)(time_ms() - start) < duration_ms) { + syscall(SYS_halt, 0); + } +} + +int main(void) { + syscall(SYS_set_beep, 10000); + wait_ms(1000); + + syscall(SYS_set_beep, 15000); + wait_ms(1000); + + // syscall(SYS_set_beep, 0); + return 0; +}