USING ASSEMBLER TO PROGRAM SYSTEM DEVICES

We will illustrate the use of Assembler in programming system devices, by describing a program to play a tune on the Computer’s loudspeakers. To do this we need to send signals to the loudspeakers that correspond in frequency and duration to each note of the tune involved.

For those completely unfamiliar with digital circuitry, here are some elementary definitions:

Signals are transmitted from a pin of one chip to the pin of another chip (or another pin on the same chip) by means of wires connecting the two pins. The transmitting pin sets the wire’s voltage to one of two values, one of which represents a binary zero, and the other a binary one.

A set of wires that together carry out some function are referred to as a bus. For instance the Pentium’s set of 32 wires used to transmit double words of data is referred to as the data bus. Single bytes can be transmitted using the lower order 8 of these wires.

A clock signal is one of the form:

Here the vertical axis is voltage, and the horizontal axis is time.

The frequency of the signal is the no. of cycles per second

For example, a frequency of 10 hz (hertz) means one with 10 cycles per sec.

Dividing such a frequency by (say) 2, would mean obtaining a signal of half the frequency.

Here is a picture of the part of the computer’s circuitry relevant to our program.

A description of the individual components follows.

The devices in this circuit, other than the speaker, consist of the interrupt controller, the timer, and the ppi (programmable preripheral controller). Each of these three chips is a general purpose one, which can be used in many kinds of circuits, including non-computer ones. The chips have to be programmed in order to get them to provide the function required of them in a particular circuit.

All the chips are connected to the Pentium via the data bus (not shown in the diagram). The Pentium programs the chips on power-up by sending them appropriate commands, in the form of specific bytes via the data bus. The chips themselves contain one or more registers to which bytes can be sent. Each of these registers has a unique number associated with it, called its port no.

To send a byte to e.g. port 43h, one should put the byte into AL, and then employ the instruction: OUT 43h, AL

An example of programming applied to the interrupt controller are commands which get it to assign precedence to the different kinds of interrupts. For instance it assigns a higher precedence to interrupts from the timer than to interrupts from the keyboard. This means that the interrupt routine for the timer cannot be interrupted by one for the keyboard, but the interrupt routine for the keyboard can be interrupted by one from the timer. The motivation in this case is not to miss any timer ticks, with consequent error to the time of day maintained. The interrupt controller is programmed by the operating system on power-up. (Our sample program does not involve programmingthis device.)

The timer chip contains 3 independent clocks. Each clock has an output pin and a pin for it’s “gate” input. The clock only transmits from its output line when the gate input is at the voltage representing a binary one, i.e. the gate input acts as a switch, able to switch the clock on or off. The input signal to the timer is 1,193,180 hz. To get one of the clocks to e.g transmit at ¼ this frequency, one needs to send the “divisor” 4 to the clock’s port. Before doing this, it is necessary to initialize the clock by means of a command sent to the timer’s control port. An example of such an initialization command is the following:

MOV AL, 1 0 1 1 0 1 1 0 b

OUT 43h, AL ; 43h is the timer’s control port no.

EXAMPLE. To produce a 1000 hz square wave on the output of clock 0. Note that, from the circuit above, this will lead to 1000 interrupts (timer ticks) per second initiated by the timer.

MOV AL, 0 0 1 1 0 1 1 0 b ; same as example above,

; except its for clock 0.

; the input signal to the timer is 1,193,180 hz. To get clock 0 to output at

; 1000 hz, we need to employ 1193 as a divisor (the error involved is negligible).

; To do this we send in succession to clock 0 (port 40h), the low order byte of

; of the number 1193, and then the high order byte (1193 = 4A9h).

MOV AX, 1193

OUT 40h, AL

MOV AL, AH

OUT 40H, AL

The PPI (programmable interface) chip, has a number of ports associated with it. We will be only concerned with port 61h, which is programmed by the operating system at power-up to be an output port. Any byte sent to port 61h, appears on the 8 associated output pins. The pin associated with bit 0 of the byte is connected to the gate input for timer clock 2, and accordingly switches the clock off or on according to whether the value of that bit is 0 or 1 respectively. The pin associated with bit 1 of the byte similarly switches the speaker off or on according to that bit’s value. We need to avoid altering the other six bits of the byte, since they serve purposes unrelated to our program, such as e.g. unlocking/locking the keyboard.

DESCRIPTION OF OUR SUBROUTINE SOUND

A main program should successively call the subroutine sound.asm for each of thenotes of the tune to be played, passing to it, as arguments, the frequency corresponding to the note, and the duration in milliseconds that the note is to be played. sound.asm implements the frequencies required by sending the appropriate divisors to clock 2. Timing the duration of the notes presents a problem, since the normal timer ticks, provided by clock 0, occur at a rate of 18.2 per sec. This means that the interval between tics is about 55 millisecs. This does not provide an accurate enough method for timing the durations required by the notes, most of which are 250 millisecs. So, for this purpose only, the subroutine alters the frequency of the timer tics to 1000 per sec (so that there is one tick per millisec). There is a DOS function for reading the time of day, but this would be too slow (and cumbersome) to use. Instead the subroutine measures durations by directly examining the count of the number of ticks that have occurred since midnight. This count is maintained by the computer in a double word at (absolute) address 46Ch. Recall that with each timer tick, the interrupt routine involved adds one to this count. sound.asm switches on the speaker and timer 2 to start playing the note, and switches them off when the note’s duration has expired. It does this by altering the low order 2 bits of port 61h of the PPI.

In order to put the count of ticks since midnight , which is stored at abolute address 46Ch, into (say) register edx, we cannot employ the code:
mov edx, 46Ch, as the Assembler translator takes the 46Ch as an offset in the data segment. It is necessary to define a new segment starting at absolute zero.

For this purpose we put 0 into ES, and then employ mov edx, ES: 46Ch