北邮软工的宝纸们看这里~汇编语言电子琴完整代码(按老师要求修改后版)

一.实验目的
掌握输入/输出指令的使用方法,并且完成一个具有复杂程序结构的输入/输出汇编程序。
二.预备知识
1、乐曲简谱中的每个音符及其节拍,在微机中对应了扬声器的发声频率和持续时间。
2、如何使 PC 机的扬声器发出指定频率的声音?下面简单介绍一下 PC 机的发声原理:
IBM-PC 系列机的主机箱装有一个小扬声器,系统板上的定时器 8253(或 8254)利用工作方式
3 产生一定频率信号,通过可编程的并行外围接口芯片 8255(或 8255A)控制其发音。
可编程的并行接口芯片 8255 有三个 8 位的并行端口:A 口、B 口和 C 口。在 IBM 系列微机中,
BIOS 在开机自检后已将 8255 初始化为 A 口和 C 口用于输入,B 口用于输出。B 口的 I/O 端口地址
61H
由图可见,8255 的 B 口的低两位用来控制扬声器驱动,当 61H 端口的 D0 位为“1”时 ,控制
8254 定时器产生驱动扬声器发声的音频信号,该位为“0”则不发信号。8254 有三个定时器,分为 0 号、1 号和 2 号定时器,驱动扬声器的是 2 号定时器,该定时器工作在方式 3,是一个频率发生器,
它负责向扬声器发送指定频率的脉冲信号。
输出端口 61H 的 D1 位为“1”或为“0”时 ,将使控制驱动器的与门电路接通或关闭,使 8254
所发出的音频信号能到达驱动器或被阻断。这样通过控制 D1 位的变化,可使扬声器接通和断开,控
制扬声器是否能发出声音。此外,通过控制 D1 位的通断时间,就能发出不同的音长。
故当 8255 输出端口 61H 的 D1 位为“1”时,在 61H 的 D0 位为“1”,8254 发出指定频率
的声音信号的前提下,声音信号通过与门到达驱动器驱动扬声器发声。即是,如要 8255 控制 8254
的 2 号定时器驱动扬声器发声,则需要的汇编命令如下:
OR AL, 00000011B
OUT 61H,AL
关闭扬声器的汇编命令如下:
AND AL, 11111100B
OUT 61H,AL
同时,定时器 8254 的 2 号定时器使用 1.19MHz 的基准频率,故若要 8254 驱动扬声器发出指
定频率的声音,则需要向 2 号定时器的计数常数寄存器(即 I/O 端口 42H)存放基准频率除以指定
频率的商(即 122870H/指定频率),该商需分两次送往 I/O 端口 42H,先送商的低字节,再送商
的高字节。同时,在使用定时器 8254 的 2 号定时器之前,需要初始化,即往 8254 的 2 号定时器
的控制寄存器(即 I/O 端口 43H)写控制字 0B6H:
MOV AL, 0B6H
OUT 43H,AL
3、键盘扫描码
(1)使用 IN AL, 60H 指令将会得到按键的扫描码,并存储在 AL 中。
(2)键盘扫描码包含通码(mark)和断码(break)两种。
(3)当按下某个按键时,产生 mark 码,并产生一次 IRQ1 键盘中断;放开按键时,产生 break
码,并产生一次 IRQ1 键盘中断。因此一个按键从按下到放开,实际上共产生了两次键盘中断。
(4)break 码和 mark 码的关系:break 码是由 mark 码最高位置 1 得来,即:break = mark
+ 0x80。
(5)使用键盘扫描码可以检测某个按键是否被按下或者弹起。
(6)实验所需的按键扫描码如下:
4、调试须知
(1)可在 EMU8086 里面进行程序的编辑和调试,但是最终需要在 DOSBox 里面使用
MASM+LINK 进行编译和链接。注意:MASM 对语法的要求会更加严格。
(2)极个别的 Windows 系统的个人计算机如果在调试过程中,出现代码编写正确但听不到声
音的情况,这是模拟 DOS 的缘故,需要到实地址的 DOS 平台上才能听到声音。建议使用 Ubuntu
(虚拟机亦可)+ DOSBox + MASM 的方式进行程序调试,此种方法不存在以上问题。附 DOSBox
for Ubuntu 官方下载地址:https://packages.ubuntu.com/bionic/dosbox
三.实验内容
试设计一个程序,能够使用键盘中字母键模拟钢琴按键发音。其中,按照字母在键盘中的排列方
式,字母键 z/x/c/v/b/n/m 分别发出低 1—低 7 共 7 个低音音符,字母键 a/s/d/f/g/h/j 分别发
出中 1—中 7 共 7 个中音音符,字母键 q/w/e/r/t/y/u 分别发出高 1—高 7 共 7 个高音音符。 按 ESC
键退出程序 当按键按下时持续发音,当按键弹起时停止发音。
四.实验代码
; multi-segment executable file with function calls

data segment
    ; 定义各音调频率
    high dw 524, 587, 659, 698, 784, 880, 988, 0 ;高音
    middle dw 262, 294, 330, 349, 392, 440, 494, 0 ;中音
    low dw 131, 147, 165, 175, 196, 220, 247, 0 ;低音
    pair_1 db 10h,1eh,2ch,90h,9eh,0ach  ;键盘值有效范围 起始
    pair_2 db 16h,24h,32h,96h,0a4h,0b2h  ;键盘值有效范围 结束
    frequency dw 0012H, 2870H ;基准频率
data ends

code segment
assume cs:code, ds:data

;子程序  
;检查al值是不是在[pair_1[bx],pair_2[bx]]范围内
;结果在cx中,如果在范围内cx=1,否则cx=0
is_in_range proc 

    mov cl,pair_1[bx]
    cmp cl, al
    ja out_range
    mov cl,pair_2[bx]
    cmp al, cl
    ja out_range
    mov cx, 1
    ret

out_range:
    mov cx, 0
    ret
is_in_range endp


start:
    mov ax, data
    mov ds, ax

    ; 初始化声音的子程序
    call init_sound

m_loop:
    call ch_keypress
    cmp al, 01H       ; 如果按下Esc则退出
    je quit

    ; 处理不同按键
    call pro_keypress
    jmp m_loop

quit:
    call stop_sound
    mov ax, 4C00H
    int 21H

init_sound proc
    ; 初始化8253控制字
    mov al, 0B6H
    out 43H, al
    ret
init_sound endp

ch_keypress proc
    in al, 60H
    ret
ch_keypress endp

pro_keypress proc
    ; 忽略无关按键 循环测试每一对值

    mov dx,0
in_loop:
    add dx,1

    mov bx,dx
    sub bx,1

    call is_in_range

    cmp cx,1
    je normal

    cmp dx,6
    jbe in_loop

    ret

normal:
    ; 大于90则是break码,停止发声
    cmp al, 90H
    jb get_offset
    call stop_sound
    ret

get_offset:
    call cal_offset
    call generate_sound
    ret
pro_keypress endp

cal_offset:
    ; 获取音调频率在内存中的偏移量
    ; si: 0高音 1中音 2低音
    mov si, 0
check:
    cmp al, 16H
    jle finish
    sub al, 0EH
    inc si
    jmp check
finish:
    sub al, 10H
    ; 如果小于0则不对,不能发声
    cmp al, 0
    jl skip
    ret

;子程序
generate_sound proc
    ; 通过si,al获取频率
    mov cl, 4
    shl si, cl
    shl al, 1
    mov ah, 0
    mov bx, si
    mov si, ax
    mov cx, high[bx][si]
    mov dx, frequency ; 基准频率高字节
    mov ax, frequency[2] ; 基准频率低字节
    div cx ; 基准频率除以音调频率
    ; 商分两次传送
    out 42H, al
    mov al, ah
    out 42H, al
    ; 发声
    or al, 03H
    out 61H, al
    ret
generate_sound endp

;子程序
stop_sound proc
    ; 停止发声
    and al, 0FCH
    out 61H, al
    ret
stop_sound endp

skip:
    ret

code ends
end start

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值