MP3
Machine Problem 3: Chat 291
| Assigned |
9:00 AM, Monday, October 8, 2001 |
| Purpose |
Text-Mode Graphics, Serial communication, and Keyboard Interrupts |
| Points |
75 |
| Due Date |
Monday, October 22 - 5:00pm |
Introduction
This MP will introduce you to text-mode video, interrupt service
routines, and serial communication. You will need to develop a firm
understanding of these concepts in order to complete it.
Problem Description
In this MP, we're going to hook into the computer's keyboard
and timer interrupts, to run some of the code asynchronously from the main
loop. Asynchronously means, in this sense, that you never know ahead of time
when it will run. You could have two move opcodes, or a compare and a
conditional jump, and either could be interrupted to run the Interrupt Service
Routine (ISR). More on this shortly.
Implementation
Interrupt Service Routines
Hardware Interrupts are generated when various hardware events occur.
Examples include the timer, which triggers 18.2 times per second, the
keyboard, which triggers for every key press and release, the serial port,
which depending on the interrupt enable register, triggers each time the
receive buffer is full, and the mouse which
triggers for any movement or button press or release. These interrupts are the
hardware's way of saying "I need attention," so that the rest of the
time the CPU can completely ignore such devices, and just execute code.
When the interrupt happens, the CPU is informed. The current opcode
finishes execution, and then the current program is interrupted. This is when
your interrupt service routine (ISR) is executed. As it happens between two effectively random lines of your program,
the ISR needs to save all registers and the flags. The hardware takes care of
part of this (by pushing of
the flags); the rest you must take care of by using IRET instead of RET (to pop the
flags), and saving your registers like normal.
Text-Mode Video
We will be using memory mapped I/O. Which means that you will want to
move 0B800h into ES and explicitly make it your segment when writing to
the screen. e.g. [es:bx]. Remember that each character in text-mode
video has both an attribute and an ascii component. The size of the
screen is 80 by 25 characters. Consult lecture eleven of this semester
for more details on how text-mode video works. We will also be calling
interrupt 10h with ah=2 to set the cursor position in several functions.
The Keyboard ISR
When our keyboard handler is called, we know a key has been pressed or
released. Unfortunately, we don't know what key, whether it was pressed or
released, and whether the press is a true press or an auto-repeat press.
There's no way for us find out the last (except keeping track), but
as for which key and press or release, we can (and must) find this out by
asking the keyboard for the scancode. See section 10.1.2.2 of the lab
manual for information on retrieving these scancodes. The
pages after that describe how scancodes work.
If you look at the scancodes and then at the ascii table, the first
thing you'll probably notice is that there's no connection. If you then look
down at the keyboard, however, you may notice a general correlation between
the scancode and the location of the key, with ESC being scancode 1, and the
numbers following an increasing pattern. We will be using two look-up tables
to convert the scancodes into their ascii equivalent. We will also
maintain two bits to tell us the state of the shift keys at all times. One
bit will tell us whether the left shift is currently pressed and the other the
same information about the right shift. Of course we have to update the
bits accordingly when shift keys are pressed or released.
Serial Communication
There is a simple asynchronous transfer protocol that governs how data is
transmitted over the serial port. Basically there is a port, either
COM1BASE or COM2BASE, that is written the data value to be transmitted.
The other port is read by the receiving program after it determines that there
is something to be read. To determine this, it just looks at a certain
bit of a register at another port. I am being deliberately vague to get
the main idea of polled I/O across. Essentially, check a certain bit to
determine if there is data available to read; if there is then read it.
Well your job in this MP will be harder because instead of polled I/O you
will be using interrupt driven I/O. But it is a very similar concept. It
boils down to setting up an interrupt service routine (ISR) that gets triggered
if there is data available to be read; if there is then read it and say that you
did. The hard part is setting up the ISR in the first place. What
this entails is setting the baud rate, basically how fast the information is
being sent, the data size, unmasking the ISR, and setting up information about
when the ISR is triggered. Each of these things is done by writing to I/O
ports. Looking in section 10.1.2.2 will show you examples of writing to
and reading from ports.
Okay now to get down to the details. I got the following table from http://www.beyondlogic.org/serial/serial.htm
| Base Address |
DLAB |
Read/Write |
Abr. |
Register Name |
| + 0 |
=0 |
Write |
- |
Transmitter Holding Buffer |
| =0 |
Read |
- |
Receiver Buffer |
| =1 |
Read/Write |
- |
Divisor Latch Low Byte |
| + 1 |
=0 |
Read/Write |
IER |
Interrupt Enable Register |
| =1 |
Read/Write |
- |
Divisor Latch High Byte |
| + 2 |
- |
Read |
IIR |
Interrupt Identification Register |
| - |
Write |
FCR |
FIFO Control Register |
| + 3 |
- |
Read/Write |
LCR |
Line Control Register |
| + 4 |
- |
Read/Write |
MCR |
Modem Control Register |
| + 5 |
- |
Read |
LSR |
Line Status Register |
| + 6 |
- |
Read |
MSR |
Modem Status Register |
| + 7 |
- |
Read/Write |
- |
Scratch Register |
Table 5 : Table of RegistersAll the registers
associated with a certain port are just offsets from that port's base
address. For the two ports we will be using the base addresses are given
as constants, namely COM1BASE and COM2BASE. There are actually twelve
logical registers associated with each serial port, but only eight actual
registers. This is possible because depending on the value of DLAB, some
registers, base address+0 and +1, mean two different things. Also a
register may have a different meaning depending on whether the operation is a
read or write. Incidentally, the page I got this table from is a pretty
decent resource.
DLAB is just the most significant bit of the Line Control Register and can
thus be set or cleared rather easily simply by writing to port COMxBASE+3.
The least significant bit of the Interrupt Enable Register when set causes the
ISR to be jumped to only when the Receiver Buffer is full.
| Speed (BPS) |
Divisor (Dec) |
Divisor Latch High Byte |
Divisor Latch Low Byte |
| 50 |
2304 |
09h |
00h |
| 300 |
384 |
01h |
80h |
| 600 |
192 |
00h |
C0h |
| 2400 |
48 |
00h |
30h |
| 4800 |
24 |
00h |
18h |
| 9600 |
12 |
00h |
0Ch |
| 19200 |
6 |
00h |
06h |
| 38400 |
3 |
00h |
03h |
| 57600 |
2 |
00h |
02h |
| 115200 |
1 |
00h |
01h |
Table 6 : Table of Commonly Used Baudrate DivisorsI
got this table from the same website. We will want a baud rate of 9600 BPS, so
we need to clear the Divisor Latch High Byte and set the Divisor Latch Low Byte
to 0Ch. Luckily, the Divisor Latch High Byte defaults to 00h, so we only
need to set the Low Byte. Also the IRQs for both ports start masked.
What this means is that those IRQs are essentially out of use. To unmask
them just write a 0 in port 21h. This feels kind of sloppy but it does the
job.
FIFO
A queue is a very simple data structure. It can be modeled by an array,
an offset into it representing the oldest element, and a variable representing
the number of elements currently in the array. Of course the elements in
the array are cyclically ordered. The rest of the details I am sure you
can figure out on your own.
The Lookup Tables
Lookup tables are an efficient way to translate one set of inputs to another
set of outputs as seen with currency conversions in MP2. In this MP
QwertyNames and QwertyShift will be used to translate a scancode into its
appropriate ASCII, while colorTable will be used to translate the function keys
to the appropriate color. The outputs for the ascii values of the function
keys from the lookup tables have been arbitrarily set to be 200 through
209. In this MP we will be using the following two
lookup tables:
QwertyNames
- This table is designed to facilitate converting scan codes into
their equivalent ascii characters.
QwertyShift
- This table is designed to facilitate converting scan codes into
their equivalent ascii shift characters.
colorTable
- Each entry in colorTable is a byte representing an attribute in
text-mode video.
Procedures
-
This assignment has 13 procedures. You will receive credit for this
assignment by replacing each of the thirteen procedures listed below with
your own code.
-
Experiment with the working code to gain a full understanding of how the
program works.
-
Use defined constants/variables where appropriate.
-
Comment ALL the functions and make sure that they preserve all register
values. Be sure to include function headers for ALL functions that
you write.
-
MP3Main
-
Purpose : Setup the ISRs and handle each key press.
-
Inputs : None
-
Outputs : None
-
Calls :
InstallKeyboard, InstallPort, DrawBorder, GetNextKey,
TypeKey, RemovePort, RemoveKeyboard, Interrupt 10h ah=2
-
Notes :
- Sets up position of cursor to be the upper left corner of the top text box
by using int 10h.
- In main loop, wait for a keypress, then handle the case when the key pressed
was one of the function keys; lastly, if it wasn't a function key
pressed, type the key to the screen
- You will want to install and uninstall your interrupt service routines as
well.
- DrawBorder
-
Purpose : Draw the rectangular box that represent the two text windows
-
Inputs : cx = offset of upper left corner of location to start displaying
the text box
-
Outputs : None
-
Calls : None
-
Notes :
- Use ATTR_BORDER as the attribute, and consult an ascii table to determine
the ascii values of the characters in the border.
- InstallPort
-
Purpose : Sets up the baud rate, data size, saves old ISR address,
installs new ISR, unmasks all IRQs, and sets up the trigger for the interrupt
-
Inputs : None
-
Outputs : None
-
Calls : Interrupt 21h
-
Notes :
- Use IRQ3 or 4 depending on the port
- [recvPort] = the base address of the correct communication port to use
- Remember to set DLAB in order to access the divisor latch register
- Remember to set the data transfer size to 8 bits, by writing PARAMS to LCR
- Save and set ISRs to [PortV] as you did in InstallKeyboard
- In order to unmask all IRQs, write a 0 to port 21h
- Set the interrupt enable register appropriately so that an interrupt is
triggered when the receive register is full
-
Points : 4
- RemovePort
-
Purpose : To restore the old handler and to mask the IRQs corresponding
to the serial port.
-
Inputs : None
-
Outputs : None
-
Calls : Interrupt 21h
- PortISR
-
Purpose : To add to the FIFO any data received over the serial port
-
Inputs : None
-
Outputs : None
-
Calls : None
-
Notes :
- Examine [recvPort] to figure out which port address to read from
- Before accessing the [recvPort], it might be smart to clear DLAB and set
transfer to 8bits, although this may not be necessary
- Use [bufsize] and [bufhead] to determine if [recvBuf] is full; if it isn't,
then use them to determine the proper spot to add the next entry---basically
you are simulating an queue
- Remember to update [bufsize] if necessary
- See Lecture 14
for more information on writing and installing ISRs
- Don't forget to send a generic end of interrupt by writing 20h to port 20h,
just like in the KeyboardISR
-
Points : 5
- InstallKeyboard, RemoveKeyboard
-
Purpose : These procedures Install and Remove the Keyboard ISR
-
Inputs : None
-
Outputs : None
-
Calls : Interrupt 21h
-
Notes :
-
Points : 3
- KeyboardISR
-
Purpose : Set [nextKey] to be the proper ASCII representation of the
character typed.
-
Inputs : None
-
Outputs :
None
-
Calls : None
-
Notes :
- See the lab manual for a skeleton ISR
- Set [quit] to 1 if escape is pressed
- Remember to check for key presses and releases (highest bit of scancode
set => key release)
- Upon a key press access the QwertyNames or QwertyShift look-up table to
determine the correct ASCII representation of the input
- The least significant bit of the [shift] variable is set if RSHIFT is
currently pressed and zero otherwise; similarly with the second least
significant bit of [shift] for LSHIFT
- If input is shift, then update the shift state correctly by modifying
[shift]
- Upon a key release, if the key released was a shift then adjust the shift
state accordingly
- Otherwise set [nextKey] to the correct ASCII representation of the typed
key
- GetNextKey
- Purpose : Polls two buffers to get the next key
to be displayed onto the screen.
- Inputs : None
- Outputs : al = ascii of next key, dx =
TOP_OFF if user typed, else BOTTOM_OFF
- Calls : TransmitKey
- Notes :
- Exits if [quit] is nonzero; otherwise it loops until [nextKey] is
nonzero, or the [recvBuf] FIFO is non-empty.
- If [nextKey] is nonzero, then it transmits it to
the other user and clears [nextKey]
- It also sets dx appropriately depending on where
the input came from..
- Points : 10
- TransmitKey
- Purpose : Transmits the data in al onto the
serial port
- Inputs : al = ASCII char to transmit
- Outputs : None
- Calls : None
- Notes :
- Use [recvPort] as always
- This function is very short.
- Points : 2
- DrawNewLine
- Purpose : Clears a line of text on the screen
- Inputs : di = offset of a location in the line
directly above where the new line will go, dx = offset of upper left
corner of text box
- Outputs : al = row#, ah = col#, di = offset of
next character
- Calls : None
- Notes :
- Remember the row, and col#'s start at 0
- Remember not to zero the attribute when clearing a line.
- Remember di is always the offset from the upper left corner of the
screen.
- DrawBackspace
- Purpose : Backspaces and moves the cursor back
- Inputs : di = offset of character from which to
backspace, dx = offset of upper left corner of text box
- Outputs : di = offset of next character
- Calls : Interrupt 10h ah=2
- Notes :
- Remember the library doesn't backspace unto the previous line, so
you don't have to either.
- The library doesn't use dx, but if you want to implement
backspacing to the previous line you will need dx.
- Remember di is always the offset from the upper left corner of the
screen.
- TypeKey
- Purpose : To display at the proper location the
input ascii value
- Inputs : al = input ascii value, di,si = offsets
of current locations in top and bottom text box respectively, dx=offset
of upper left corner of the correct text box
- Outputs : di,si = offset of next character's in
the appropriate text box
- Calls : DrawBackspace, DrawNewLine, Interrupt
10h ah=2
- Notes :
- Check whether input is backspace or if input is enter, or if
current location is at the end of a line; in each case call the
proper function with the correct inputs
- Notice DrawBackspace takes care of the cursor for you while
DrawNewLine does not
- Otherwise just output the character and cursor as normal
- Remember di is always the offset from the upper left corner of the
screen.
- Points : 8
Monitor the newsgroup and this on-line section for revisions to the
MP or to the write-up
General Notes
- The serial communication cable that you will need may not already be
hooked-up to a free computer in the lab. If this happens just talk to
a TA.
- Remember to save and restore all modified registers that are not outputs.
- Remember to start early! This MP has more difficult concepts, but when you
understand them, it is easier to code then the last one.
- Monitor the newsgroup! There may be corrections made on this
write-up or on the newsgroup.
Procedure
Final Steps
1. Demonstrate your MP3.EXE to a TA. You may be asked to recompile and demo
the program. Your program must work with all given input.
2. Be prepared to answer questions about any aspect of the operation of your
program. The TAs will not accept an MP if you cannot fully explain your code and
your implementation. Delayed MPs will be subject to late penalties as described
in the course syllabus (10/pts per day).
3. The TA will complete the code submission procedure.
|