diff --git a/app/config.c b/app/config.c index 3390160..2c2f946 100644 --- a/app/config.c +++ b/app/config.c @@ -102,6 +102,7 @@ bool enable_sm = true; bool enable_bench = true; bool enable_mch_read = true; bool enable_numa = false; +bool enable_sound = false; bool enable_ecc_polling = false; @@ -360,6 +361,8 @@ static void parse_option(const char *option, const char *params) } else if (strncmp(params, "high", 5) == 0) { power_save = POWER_SAVE_HIGH; } + } else if (strncmp(option, "sound", 6) == 0) { + enable_sound = true; } else if (strncmp(option, "trace", 6) == 0) { enable_trace = true; } else if (strncmp(option, "usbdebug", 9) == 0) { diff --git a/app/config.h b/app/config.h index 8d23a22..d229855 100644 --- a/app/config.h +++ b/app/config.h @@ -65,6 +65,7 @@ extern bool enable_bench; extern bool enable_mch_read; extern bool enable_ecc_polling; extern bool enable_numa; +extern bool enable_sound; extern bool pause_at_start; extern bool dark_mode; diff --git a/app/display.c b/app/display.c index 54462ff..31b1065 100644 --- a/app/display.c +++ b/app/display.c @@ -30,6 +30,8 @@ #include "tests.h" +#include "sound.h" + #include "display.h" //------------------------------------------------------------------------------ @@ -463,6 +465,10 @@ void display_big_status(bool pass) return; } + if (enable_sound) { + sound_beep(pass); + } + save_screen_region(POP_STATUS_REGION, popup_status_save_buffer); set_background_colour(palette.popup_background); @@ -647,6 +653,9 @@ void do_tick(int my_cpu) if (update_spinner) { spin_idx = (spin_idx + 1) % NUM_SPIN_STATES; display_spinner(spin_state[spin_idx]); + if (enable_sound) { + sound_tick_task(); + } } // This only tick one time per second diff --git a/app/sound.c b/app/sound.c new file mode 100644 index 0000000..9d58eab --- /dev/null +++ b/app/sound.c @@ -0,0 +1,47 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (C) 2024 Anton Ivanov (aka demiurg_spb+rigler). +// Copyright (C) 2024 Lionel Debroux. +#include +#include "pit.h" +#include "sound.h" // self to check prototypes + +static bool enabled; +static unsigned int duration; + +#define DURATION (7) + +static void beep_off() +{ + if (enabled) { + enabled = 0; + duration = 0; + pit_off(); + } +} + +static void beep_on(unsigned f_hz) +{ + if (!enabled) { + enabled = 1; + duration = DURATION; + pit_init_square_wave_generator(2, f_hz); // pit-ch2 is connected to buzzer + } +} + +void sound_beep(bool ok) +{ + #define beep_freq_ok (1100) + #define beep_freq_er ( 380) + + beep_on(ok ? beep_freq_ok : beep_freq_er); +} + +void sound_tick_task(void) +{ + if (duration) { + duration--; + } + else { + beep_off(); + } +} diff --git a/app/sound.h b/app/sound.h new file mode 100644 index 0000000..7d525ce --- /dev/null +++ b/app/sound.h @@ -0,0 +1,18 @@ +// SPDX-License-Identifier: GPL-2.0 +#ifndef SOUND_H +#define SOUND_H +/** + * \file + * + * Provides types and variables used when performing the memory tests. + * + * Copyright (C) 2024 Anton Ivanov (aka demiurg_spb+rigler). + */ + +#include + +void sound_beep(bool ok); +void sound_tick_task(void); + + +#endif // SOUND_H diff --git a/build/i586/Makefile b/build/i586/Makefile index 8158415..29e77a9 100644 --- a/build/i586/Makefile +++ b/build/i586/Makefile @@ -86,6 +86,7 @@ TST_OBJS = tests/addr_walk1.o \ APP_OBJS = app/badram.o \ app/config.o \ + app/sound.o \ app/display.o \ app/error.o \ app/main.o \ diff --git a/build/x86_64/Makefile b/build/x86_64/Makefile index af5b6ef..52d44de 100644 --- a/build/x86_64/Makefile +++ b/build/x86_64/Makefile @@ -87,6 +87,7 @@ TST_OBJS = tests/addr_walk1.o \ APP_OBJS = app/badram.o \ app/config.o \ + app/sound.o \ app/display.o \ app/error.o \ app/main.o \ diff --git a/system/pit.h b/system/pit.h new file mode 100644 index 0000000..5240cd7 --- /dev/null +++ b/system/pit.h @@ -0,0 +1,105 @@ +// SPDX-License-Identifier: GPL-2.0 +#ifndef PIT_H +#define PIT_H +/** + * \file + * + * Provides types and variables used to work with PIT + * + * This will work on real hardware and in QEMU if it started with -audiodev pa,id=speaker -machine pcspk-audiodev=speaker. + * The code also changes the PIT timer 2 frequency, so you will have to reset that when you're done "beep"ing :) + * + * hints: + * https://wiki.osdev.org/PC_Speaker + * https://wiki.osdev.org/Programmable_Interval_Timer + * https://stackoverflow.com/questions/8960620/low-level-i-o-access-using-outb-and-inb + * https://stackoverflow.com/questions/14194798/is-there-a-specification-of-x86-i-o-port-assignment + * https://stackoverflow.com/questions/22355436/how-to-compile-32-bit-apps-on-64-bit-ubuntu + * + * outb() and friends are hardware-specific. The value argument is + * passed first and the port argument is passed second, which is the + * opposite order from most DOS implementations. + * + * void outb(unsigned char value, unsigned short port); + * + * Copyright (C) 2024 Anton Ivanov (aka demiurg_spb+rigler). + * Copyright (C) 2024 Lionel Debroux. + */ + + +#include +#include // if file not found for 32bit-target in 64bit OS: "sudo apt install gcc-multilib g++-multilib" + +#define PIT_CH0_PORT (0x0040U) // r/w +#define PIT_CH1_PORT (0x0041U) // r/w +#define PIT_CH2_PORT (0x0042U) // r/w +#define PIT_CTL_PORT (0x0043U) // write only +#define PIT_STATUS_PORT (0x0061U) + +#define PIT_STATUS_MASK (0b00000011) + +#define _PIT_CTL_CH(x) (((x)&3U)<<6) // 0-ch0, 1-ch1, 2-ch2, 3-Read-back command (8254 only) + +#define PIT_CTL_CH0 _PIT_CTL_CH(0) +#define PIT_CTL_CH1 _PIT_CTL_CH(1) +#define PIT_CTL_CH2 _PIT_CTL_CH(2) +#define PIT_CTL_READ_BACK _PIT_CTL_CH(3) + + +#define _PIT_CTL_ACCESS(x) (((x)&3U)<<4) // 0-Latch count value command, 1-lbyte, 2-hbyte, 3-lobyte/hibyte + +#define PIT_CTL_ACCESS_LATCH_CNT_VAL _PIT_CTL_ACCESS(0) +#define PIT_CTL_ACCESS_LBYTE _PIT_CTL_ACCESS(1) +#define PIT_CTL_ACCESS_HBYTE _PIT_CTL_ACCESS(2) +#define PIT_CTL_ACCESS_LBYTE_HBYTE _PIT_CTL_ACCESS(3) + + +#define _PIT_CTL_OP_MODE(x) (((x)&7U)<<1) + +#define PIT_CTL_OP_MODE0 _PIT_CTL_OP_MODE(0) // (interrupt on terminal count) +#define PIT_CTL_OP_MODE1 _PIT_CTL_OP_MODE(1) // (hardware re-triggerable one-shot) +#define PIT_CTL_OP_MODE2 _PIT_CTL_OP_MODE(2) // (rate generator) +#define PIT_CTL_OP_MODE3 _PIT_CTL_OP_MODE(3) // (square wave generator) +#define PIT_CTL_OP_MODE4 _PIT_CTL_OP_MODE(4) // (software triggered strobe) +#define PIT_CTL_OP_MODE5 _PIT_CTL_OP_MODE(5) // (hardware triggered strobe) + + +#define _PIT_CTL_BCD_MODE(x) ((x)&1U) + +#define PIT_CTL_BIN16_MODE _PIT_CTL_BCD_MODE(0) // 16-bit binary +#define PIT_CTL_BCD4_MODE _PIT_CTL_BCD_MODE(1) // four-digit BCD + + +static inline uint32_t pit_freq2div(unsigned frequency) +{ + return (uint32_t)(0.5f + 1193180.0f/frequency); +} + +static inline void pit_off(void) +{ + const unsigned char x = inb(PIT_STATUS_PORT) & ~PIT_STATUS_MASK; + + outb(x, PIT_STATUS_PORT); +} + +static inline void pit_init_square_wave_generator(unsigned char ch, uint32_t frequency) +{ + const uint32_t clk_div = pit_freq2div(frequency); + + const unsigned char cfg = PIT_CTL_OP_MODE3 | PIT_CTL_BIN16_MODE | PIT_CTL_ACCESS_LBYTE_HBYTE | _PIT_CTL_CH(ch); + + const unsigned short PIT_DATA_PORT = PIT_CH0_PORT + ch; // base + offset + + outb(cfg, PIT_CTL_PORT); // 0xb6 = 0b10110110 + + outb((uint8_t)clk_div, PIT_DATA_PORT); // low byte + outb((uint8_t)(clk_div>>8), PIT_DATA_PORT); // hi byte + + unsigned char x = inb(PIT_STATUS_PORT); + + if (!(x&PIT_STATUS_MASK)) { + outb(x|PIT_STATUS_MASK, PIT_STATUS_PORT); + } +} + +#endif // PIT_H