提问人:STC2 提问时间:10/27/2023 更新时间:11/5/2023 访问量:61
使用 GNU Assembly 使扬声器在 Ubuntu Linux 中以定义的频率振动
Make a speaker vibrate at a defined frequency in Ubuntu Linux, with GNU Assembly
问:
我是汇编语言的新手
我们的计算机一直在发出声音(mp3、wav、mp4、ogg 等)。
如何使用汇编语言直接与说话者交流?在 C 语言中,您可以使用“Beep()”并将持续时间(以毫秒和赫兹为单位)传递给扬声器。我怎样才能用GNU汇编语言做到这一点?
我的电脑规格: 戴尔 Optiplex 960; Ubuntu 23.04 LTS x86-64 体系结构。
有人可以留下并举例说明如何做到这一点,即粘贴一些源代码吗? 对每个指令正在做的事情发表评论也将非常有帮助。
我试图找到一些教程,但我有点迷茫。大多数在 NASM(不是我使用的 GNU)中的教程都会使用系统默认的声音,即 Windows 上的铃声或 Linux Ubuntu 上的通知声音。
答:
更新:根据戴尔 Optiplex 960 的在线服务手册,内置扬声器是可选设备。因此,您的计算机可能没有连接内部扬声器,对问题的评论以及有关使用 PC 扬声器界面的答案将无济于事。这个答案主要解释了使用内置扬声器,但最后对 PCM 音频有几点评论。
由于几年来我一直在维护通用的 Linux 哔哔声实用程序 https://github.com/spkr-beep/beep,因此我建议在 Linux 上为 PC 扬声器使用正确的 API,而无需特权访问。
这相当于一个程序
open(2)
已知设备文件并保存文件描述符 如果成功,否则中止程序/dev/input/by-path/platform-pcspkr-event-spkr
- 准备一个(定义在)中,(频率将使PC扬声器静音)(可以静态完成)
struct input_event
linux/input.h
e.type = EV_SND
e.code = SND_TONE
e.value = frequency_in_Hz
0
write(2)
该文件描述符的 24 字节结构- 最终文件描述符
close(2)
所以基本上,你只需要编写一个程序,将几个字节写入一个文件。
如果希望程序开始提示音,然后等待,然后停止该提示音,则程序还需要等待定义的时间。带有 from 的系统调用可以为您提供帮助。nanosleep(2)
struct timespec
time.h
这样一来,您只需要弄清楚如何执行系统调用,然后。好吧,如果你之后无论如何都不需要,但可能也需要。open(2)
write(2)
close(2)
close(2)
exit(2)
nanosleep(2)
汇编中的任何 Linux“hello world”程序都应该是一个很好的起点:它已经实现了 和 ,这应该是添加其他系统调用时的好起点。write(2)
exit(2)
从一个简单的hello world程序开始,如 https://cs.lmu.edu/~ray/notes/gasexamples/(在搜索引擎中首次点击“linux gnu assembly hello world”),从 https://blog.rchapman.org/posts/Linux_System_Call_Table_for_x86_64/(在搜索引擎中首次点击“x86-64 abi”)的一些信息开始,加上一些我自己的装饰(、、、、和调试),给出以下程序,它发出哔哔声。停止哔哔声留给读者作为练习,或用于在外壳上运行。.size
foo_size = . - foo
.balign
.global
beep
# gas-beep.s - do a PC speaker beep with GNU assembler for Linux x86-64
.global _start
.text
.global main_program
main_program:
_start:
# open(speaker_device, O_WRONLY)
mov $2, %rax # sys_open
mov $speaker_device, %rdi # filename = speaker_device
mov $1, %rsi # flags = O_WRONLY
xor %rdx, %rdx # mode = 0
syscall
cmp $0, %rax
jl error_exit_open
mov %rax, fd
# write(fd, &beepcmd_on, sizeof(beepcmd_on))
mov $1, %rax # sys_write
mov fd, %rdi # fd = fd
mov $beepcmd_on, %rsi # buf = beepcmd_on
mov $beepcmd_on_size, %rdx # count = sizeof(beepcmd_on)
syscall
# TODO: nanosleep(2) with a struct timeval aka two .quad values
# TODO: turn off sound using write(2) (beepcmd_off with frequency 0)
# TODO: print a few enlightening words to stdout with write(2)
# close(fd)
mov $3, %rax # sys_close
mov fd, %rdi # fd = fd
syscall
# exit(0)
mov $60, %rax # sys_exit
xor %rdi, %rdi # error_code = 0
syscall
error_exit_open:
# write(1, errmsg_open, sizeof(errmsg_open))
mov $1, %rax # sys_write
mov $2, %rdi # fd = STDERR_FILENO
mov $errmsg_open, %rsi # buf = errmsg_open
mov $errmsg_open_size, %rdx # count = sizeof(errmsg_open)
syscall
# exit(2)
mov $60, %rax # sys_exit
xor $2, %rdi # error_code = 2
syscall
.size main_program, . - main_program
.balign 8
.global beepcmd_on
# struct input_event {.type=EV_SND, .code=SND_TONE, .value=880}
beepcmd_on:
.quad 0
.quad 0
.short 0x12 # .type = EV_SND
.short 0x02 # .code = SND_TONE
.int 880 # .value = frequency
beepcmd_on_size = . - beepcmd_on # required for write(2)
.size beepcmd_on, beepcmd_on_size
.global speaker_device
speaker_device:
.asciz "/dev/input/by-path/platform-pcspkr-event-spkr"
.size speaker_device, . - speaker_device
.global errmsg_open
errmsg_open:
.ascii "Error: Could not open(2) the device.\n"
errmsg_open_size = . - errmsg_open # required for write(2)
.size errmsg_open, errmsg_open_size
.bss
.global fd
.lcomm fd, 8 # uninitialized file descriptor variable
为了使构建和理解它更容易,请使用以下命令来构建整个程序的符号列表、地图文件、反汇编视图等:GNUmakefile
.PHONY: all
all: all-local
TARGETS += gas-beep.lss
%.lss: %
objdump -h -S $< > $@
TARGETS += gas-beep.syms
%.syms: %
objdump --syms $< > $@
TARGETS += gas-beep.nm
%.nm: %
nm $< > $@
TARGETS += gas-beep.map
%.map: %
TARGETS += gas-beep
gas-beep: gas-beep.o
ld -g -Map=gas-beep.map -o $@ $^
TARGETS += gas-beep.lst
%.lst: %.o
TARGETS += gas-beep.stripped
%.stripped: %
strip -s -o $@ $<
TARGETS += gas-beep.readelf-a
%.readelf-a: %
readelf -a $< > $@
%.o: %.s
gcc -Wall -Werror -g -Wa,-adhlns=$*.lst -c -o $@ $<
.PHONY: all-local
all-local: $(TARGETS)
.PHONY: clean
clean:
rm -f *.{lss,lst,map,nm,o,readelf-a,stripped,syms} gas-beep
请注意,您可能需要显式安装并加载内核模块,并确保您的计算机实际上连接了物理 PC 扬声器(扬声器或压电蜂鸣器)。安装操作系统的软件包可以为您节省一些手动系统管理员工作。pcspkr.ko
beep
PC 扬声器就这么多。
如果您想通过 PCM 音频而不是使用 PC 扬声器来产生声音,那么 API 可能比仅仅向文件写入几个字节要复杂得多。几年前,为PCM音频准备一个简单的内存缓冲区,用方波连接到一个PCM音频设备,其复杂性相当。然而,如今,即使只是找到一个要打开的设备也要复杂得多,我们甚至不是在谈论缓冲区用完和重新填充缓冲区,而且有不止一个 PCM 声音 API。write(2)
open(2)
/dev/dsp
评论
Beep()
man -a Beep
void beep(void)
Beep(int)
double
beep
':'
beep -f 5000 -l 50 -r 2
sudo
.note