S010 and the Infernal Machine

Apr 05, 2009 21:32


For half of this semester we had a course called 'Microprocessors and embedded systems'. To date -- this was the most interesting course we had, such a pity that it's so short.

During the course, I kinda got a chance to look at computers from a slightly different angle -- from the angle of the computer system architect as opposed to a computer programmer. I didn't even know that such information was available somewhere (it seems to be quite harder to find than info on programming for instance). Never before have I seriously thought about questions such as what devices are there in a computer? How do they communicate? How is this all controlled? etcetc. We have had a 'The fundamentals of computer architecture' course last year, but it was rather dry and kinda abstract.

We had to do 2 lab workshops to complete the course: write a program in assembly language which outputs 10 rising tones through the PC speaker; write a program in assembly language which disables acceptance of scan-codes from the keyboard for 10 seconds or blinks the three leds of the keyboard or does a "hot"-reset of the CPU.
The peecee

The course material described the first few models of the IBM PC.
First models of IBM PC were rather simple. The microprocessor, system peripherials and system bus. We were to program in real-mode assembly. But I decided that I'll do this in protected mode in OpenBSD. And I succeeded.

All the IBM PCs are backwards compatible with all previous models. When you turn on your computer, it's basically the IBM PC from 1981. It has all the devices that IBM PC had, they're located at the same addresses, and it behaves just like an IBM PC. It's completely backwards compatible with the 1981 machine. Only when your OS loads and switches the CPU into protected mode, all the things that PC architecture gained over the dozens of years appear.



IBM PC XT



IBM PC AT
The Intel 8253 timer/counter

One of the system peripherials of the original IBM PC is the Intel 8053 timer/counter chip. It has three separate, independent channels. The registers through which one can communicate to the chip have addresses 0x40 through 0x43, in the I/O address space (that means you have to use the in/out instructions to work with them). The 0x40-0x42 are the input/ouput registers for the three channels and 0x43 -- is the control register, one for all three channels.

The zeroth channel is programmed by the ROM BIOS at the boot of the system, to cause an interrupt every 1/18 of second. The ROM BIOS installs a handler for this interrupt (which has the highest priority in the system btw), which increments a 4-byte word in the memory at the location 0x46C, which represents the current time. The first IBM PCs didn't have any non-volatile memory, so the computer operator had to enter the correct time and date at the every start of the system. Later on, a new chip was added to the architecture: the Real Time Clock. This chip had it's own battery, and was responsible for keeping the time and configuration settings between the sessions.

The first channel of the timer is used for the DRAM refreshing (dynamic RAM -- needs to be constantly refreshed, otherwise the information will be lost, is very cheap but slow; static RAM -- doesn't need refreshing and thus is much faster and simpler to work with, requires ~8 times more transistors per bit and is much more expensive). It's output is connected to the Memory Refresh Controller, and triggers a refresh run every 4-8 ms.

The second channel has no special function... Or rather, it has no critical functions assigned to it, so the programmer is free to use it as he likes. The second channel is connected to the PC speaker, and may be used to generate sounds by loading the frequency divider into the channel's counter, setting it to mode 3 and allowing the output to go to speaker in the parallel interface's register at 0x61.
The keyboard controller

In the keyboard, there is a very simple microcontroller, an Intel 8048. As I once posted, maybe like, 2 years ago, it's amazing how everything around us is surrounded by computers, even in the places where you'd never expect. A microcontroller is basically a simple but nevertheless complete computer system with a microprocessor, memory and peripherials all embedded into a single tiny chip.

So, the 8048 constantly scans the matrix of the keyboard keys to see what keys are pressed and sends this data to the system unit.

Here's a scheme:



8048 is connected to the system unit with 4 lines: +5V, 0V, DATA and CLOCK. I won't go into the details of the keyboard protocol, but if you're interested, go to http://www.beyondlogic.org/keyboard/keybrd.htm.

Surprisingly, on the other side, in the system unit, there is another microcontroller, the older brother of 8048 -- the Intel 8042 chip. Two addresses are assigned to 8042 -- 0x60 and 0x64. Executing out on 0x60 or 0x64 writes to the input register of the keyboard controller, 8042 (it may be either a command for 8042 or a command for 8048). Executing in on 0x60 yields the output register of the keyboard controller (it may be a scan code from 8048; an ACK byte; reply byte). in on 0x64 yields the status register of the controller.
The Blue Danube

I began with the PC speaker lab workshop.

I thought that playing 10 rising tones is kinda boring, so I decided to write a program which would play music. I chose The Blue Danube by Johann Strauss (the music from 2001: A Space Odyssey and later remakes of Elite).

First, I wrote a program in C which can read a file with notes and either play it on the PC speaker, either write a file with pairs of integers: frequency and duration.
The format is the following:
... e p 100 octave 4 c 200 c e g c5 p 100 octave 5 c6 200 c6 ...
Writing this program wasn't hard. What followed -- was. I knew how to gain access to the I/O ports on UNIX back from one year ago, when I made the LED thingy for LPT port. BSDs and Linux all have special system/library calls for enabling I/O port access. In OpenBSD it's i386_iopl(2) and i386_set_ioperm(2).

The hard part was to learn to program in assembly in protected mode. I mean stuff like, procedure calls, calling conventions, argument passing and general programming. Assembly language is unlike any other programming language. It requires you to change your way of thinking to program in it successfully. It requires you to understand, to feel the machine.
Code and refs

pcspkr_play.c -- the C program which plays a file with notes.
Works on OpenBSD and on Linux. Probably will work on FreeBSD and NetBSD with little or no modification.
To compile under OpenBSD: cc -li386 -o pcspkr_play pcspkr_play.c
To compile under Linux: cc -DLINUX -o pcspkr_play pcspkr_play.c

blue_danube.txt -- file with the notes for The Blue Danube.

play_blue_danube3.s -- the program in assembly which plays The Blue Danube. The notes are embedded into the source in the form of hex numbers. Will work only on OpenBSD, but can be easily modified to work on Linux.

blink_leds.s -- blinks the three keyboard leds a few times.
kbd_off.s -- turns keyboard off for the specified number of seconds.

Some places of interest:
Intel 64 and IA-32 Architectures Software Developer's Manuals
OpenBSD kernel's source code, specifically: dev/pckbc/pckbd.c, dev/ic/pckbc.c -- the keyboard driver; dev/isa/pcppi.c -- the PC speaker driver.
Programming in assembly under OpenBSD.

the force, hardware, tl;dr, programming, computers, just for fun, awesome, lulz, what does that button do?, electronics, leisure, poking

Previous post Next post
Up