There was an integrated circuit specifically used for timing in the early PC series, model 8253/8254. It has three channels, the first channel is used to control the normal operation of the system clock; the second channel is used for memory refresh; these two channels have nothing to do with the issues we are discussing now. The third channel is the most interesting, it is connected to the speaker through a set of circuits.
Figure 4-1 shows the complete sound circuit in the PC. The G terminal of timer channel 3 is connected to the bit0 bit of port 61H. If the bit0 bit of port 61H is set to 1, then timer channel 3 will be started. At this time, a set of signals will be output from the OUT terminal. The frequency of the signal can be used Program control; if the bit 0 of the 61H port is 0, the timer is turned off and the OUT terminal will be constant 1
This circuit is used here as a "controllable switch". If the bit 0 of the 61H port is , bit1 bit is set to 1, which is equivalent to turning on both the timer and the switch. At this time, the sound signal generated by the timer will be sent to the amplifier to push the speaker to make sound; if the bit0 bit is set to 0, the timer is turned off, and this When the OUT terminal is 1, if the state of bit1 is continuously changed at this time, the sound can also be heard from the speaker. This is the method we used in Chapter 2; if the bit1 bit is set to 0, the switch is turned off, even if No sound can be heard even when the timer is turned on.
This can be verified through DEBUG: enter DEBUG, type "O61 3" after "-", and you will hear a continuous sound from the speaker. (Experiment under pure DOS)
Output "03" to the 61H port, which is equivalent to turning on the timer and switch. At this time, there will be a continuous sound. The frequency of this sound is about 896Hz, which is the same as what we just The frequency of the beep heard when turning on is the same.
The interesting thing is that once the sound is emitted, it will not stop and does not interfere with any user operations.
The only way to stop this sound is to enter DEBUG and type the command "O61 0 (can also be 1 or 2)". The reason for this phenomenon is that the work of the timer does not require the direct participation of the CPU. The CPU only needs to set the working status and frequency value for the time and turn on the timer. At this time, the timer will work independently and the CPU can do it. Something else. This feature is very useful, it is the prerequisite for realizing "background music".
So how to change the frequency of sound? Please note that channel 3 of the timer also has an input terminal CLK. This terminal inputs a fixed signal with a frequency of 1193181.6Hz. The output signal has the following relationship with this signal:
---------------------------------- -----------------------------------------------
F(OUT)=F(CLK)/N
----------------------------- -------------------------------------------------- -
Where N is a 16-bit data, its value can be set by the program. The method is very simple: divide this 16-bit data into high and low 8-bits, first send the low 8 bits to the 42H port, and then send the high 8 bits to the 42H port, and the frequency of the output signal will change. We can try:
C:\ASM\gt;DEBUG[Enter]
-O61 3[Enter]
-O42 0[Enter]
-O42 3[Enter]
Set the new N value to 300H, and the corresponding F (OUT) is 1193191.6/300H=1553Hz. The voice immediately became shriller.
One thing must be explained, the timer has multiple working states, and not every working state can produce sound, so when we want to produce sound through the timer, we should first "initialize" the timer, Establish it in the correct working order. Initializing the timer is not complicated, just output data 0B6H to port 43H. The binary form of this data is 10110110. Some books call this number a "magic BYTE".
With the knowledge introduced above, we can program the timer to emit sounds at a given frequency. The program PROG6 can make the speaker produce 1000Hz sound
---------------------------------- --------------
0A3E: 0100 MOV AL, B6; AL register loads timer initialization setting code
0A3E: 0102 OUT 43 , AL; Output the setting code to the 43H port for initialization
0A3E: 0104 MOV AX, 04A9; 1193181.6Hz/1000=1193hz =04A9 hexadecimal AX register sets the N value
0A3E: 0107 OUT 42, AL; Output the N value to the 42H port in two parts because it is 8 bits
0A3E: 0109 MOV AL, AH
0A3E: 010B OUT 42, AL
p>
0A3E: 010D IN AL, 61; Get the current status of port 61H
0A3E: 010F PUSH AX; Push into the stack
0A3E: 0110 OR AL, 03; 0111
0A3E: 0112 OUT 61, AL; turn on the timer and electronic switch
0A3E: 0114 MOV AH, 01; AH = 01h Return: AL = character read waiting for input
; character is echoed to standard output (echo)
0A3E: 0116 INT 21
0A3E: 0118 POP AX; recovery 61H
0A3E :0119 OUT 61, AL
0A3E:011B RET
0A3E:011C
We have discussed how to make a sound of a determined frequency through channel 3 of the timer , in this section we are going to learn how to accurately time things so that we can solve the problem of playing music.
The timing circuit in the PC has three channels, channel 3 is used for sound generation, and channel 1 is used to control the internal clock of the system. Everyone knows very well that you can use the "TIME" command of DOS to observe and modify a clock inside the system. The reason why this clock can continue to run mainly depends on channel 1 of the timer.
Channel 1 works in the same way as channel 3, but when the system is started, it is set to send a signal with a fixed frequency of 18.2Hz. This signal is sent directly to the "interrupt controller" in the system. Each "Hz" generates a hardware interrupt, which is generally called "IRQ0", and the corresponding interrupt number is 08H. In other words, when the computer starts up, our machine looks very calm, but in fact the CPU is very busy. Under the control of the timer, an interrupt No. 08H is executed every 55 milliseconds. The main job of this interrupt is to count continuously.
There is a four-byte storage space at "0040H:006CH" in the memory dedicated to saving the count value. Every time the CPU executes an 08H interrupt, the four-byte count value is increased by 1. It is not difficult. It is calculated that every time the count value increases by 1091, exactly 1 minute has passed, and every time it has increased by 65454, exactly 1 hour has passed. The reason why the system's internal clock can keep accurate time depends on the 08H interrupt and the four-byte count value. Therefore, if we want accurate timing, we must rely on the clock count value
;---A program that can accurately emit a 1000Hz sound with a sound duration of 5 seconds-------- -------
PORTB equ 61H
code segment
assume cs:code, ds:code
org 100h
main proc near
mov al, 10110110b; initialize timer
out 43h, al
mov ax, 4a9h; set N The value is 04A9H
out 42h, al
mov al, ah
out 42h, al
in al, PORT_B; turn on timing Device and AND gate
or al, 3
out PORT_B, al
;---------------- --The following is the timing part---------------
mov ah, 0; select function 0 of the 1AH interrupt
int 1ah; Call the 1AH interrupt to obtain the current clock count
add dx, 91; add 91---5 seconds to the current clock count
mov bx, dx; save the count value when the timer expires
delay: int 1ah; call the 1AH interrupt twice to obtain the clock count value
cmp dx, bx; has the count value been reached at the end of the timer?
jne delay; If not reached, return to DELAY to continue
;--------------------- ----------------------------
in al, PORT_B; Timing termination, close timer and AND gate
and al, 0fch; 1111 1100
out PORT_B, al
int 20h; end program
main endp
code ends
Reference material: Practical explanation of PC assembly language/edited by Li Chunsheng.
data segment
freq dw 196, 220
dw 262, 262, 262, 262, 262, 220, 196
dw 262, 262, 262, 262, 294, 262, 220, 262
dw 294, 294, 294, 294, 294, 262, 220
dw 294, 294, 294, 294, 330, 294, 330, 392
dw 440, 440, 392, 440, 392, 330
dw 294, 294, 330, 294, 262, 220, 196, 220
dw 262, 262, 262, 262, 262, 220
dw 262, 196, 220
dw 440, 440, 392, 440, 524 , 440
dw 392, 330, 294, 262, 220, 196, 220
dw 262, 262, 262, 262, 294, 262
dw 262, 330, 392
dw 440, 440, 440, 440, 524, 440
dw 392, 392, 392, 440, 392, 330, 294
dw 262, 262, 262, 262, 294
dw 330, 330, 294
dw 262, 262, 262, 262, 524, 440
dw 392, 392, 392, 440, 392, 330, 392
dw 440, 524, 524, 440, 392
dw 392, 330, 392
dw 440, 440, 440, 440, 524, 440
dw 392, 392, 392, 440, 392, 330, 294
dw 262, 262, 262, 262 , 392
dw 330, 330, 294
dw 262, 262, 262, 262, 294, 330
dw 392, 392, 330, 392, 330, 392
dw 440
dw 9, 9, 196, 660, 294, 294, 262
dw 262, -1
time dw 400, 400
dw 400, 200, 400, 400, 800, 400, 400
dw 400, 200, 400, 200, 200, 800, 400, 400
dw 400 , 200, 400, 400, 800, 400, 400
dw 400, 200, 400, 200, 200, 800, 400, 400
dw 400, 800, 400, 800 , 400, 400
dw 400, 200, 200, 400, 400, 800, 400, 400
dw 400, 200, 400, 400, 800, 800
dw 1600, 800, 800
dw 400, 800, 400, 800, 400, 400
dw 400, 400, 400, 400, 800, 400, 400
dw 400, 800, 400, 800, 400, 200
dw 2400, 400, 400
dw 400, 800, 400, 800, 400, 400
dw 400, 800, 200, 200, 800, 400, 400
dw 400, 800, 400, 800, 800
dw 2400, 400, 400
dw 400, 800, 400, 800, 400, 400
dw 400, 800, 200, 200, 800, 400, 400
dw 800 , 400, 800, 400, 200
dw 2400, 400, 400
dw 400, 800, 400, 800, 400, 400
dw 400, 800, 200, 200, 800, 400, 400
dw 400, 800, 400, 800, 800
dw 2400, 400, 400
dw 400 ,8
00, 400, 800, 400, 400
dw 400, 800, 400, 800, 400, 400
dw 3200
dw 800, 400, 400 , 400, 400, 400, 400
dw 4000
data ends
code segment
assume cs: code, ds: data
main proc far
start: mov ax, data
mov ds, ax
mov si, offset freq
mov di, offset time
l1: mov cx, [si]
cmp cx, -1
je exit
mov bx, [di]
call gensound
add si, 2
add di, 2
jmp l1
exit: mov ax, 4c00h
int 21h
main endp
gensound proc near
push dx
mov al, 0b6h
out 43h, al
mov dx, 08h
mov ax, 3208h
div cx
out 42h, al
mov al, ah
out 42h, al
in al, 61h
mov ah, al
or al, 3
out 61h, al
l2: push dx
push ax
mov dx, 8h
mov ax, 0f05h
s1: sub ax, 1
sbb dx, 0
jnz s1
pop ax
pop dx
dec bx
jnz l2
mov al,ah
out 61h, al
pop dx
ret
gensound endp
code ends
end start