I have made the game Tetris using a
PIC16F84 running @ 12MHz. Tetris is an old Russian
computer game where you should try to fit in block into
a play-field, quite simple but really fun. In my
version, the video signal is generated in software. The
only hardware used for the video generation is two
resistors forming a 2-bit DA converter. Usually the
video signal is generated in video games is created with
a dedicated video chips, reading the image data from a
graphics memory. In this project the video signal is
calculated in real-time by the microprocessor as the
electron beam sweeps over the screen.
The first screen is where you select how
you want to play by moving the joystick: DOWN: Human vs.
Human (H-H), LEFT: Human vs. Computer (H-C) or RIGHT:
Computer vs. Computer (C-C). Start with FIRE.
Unfortunately it is impossible to beat the computer,
since there was not enough room to make the computer
beatable. That makes the computer vs. computer game to
play forever until someone reset the game using the
reset switch. You start serving by pressing fire, it is
also possible to change direction and speed of the ball
using fire. The player who has the serve will get
points. If the player with the serve miss the ball, then
the serve goes over to the other player. When someone
wins a game over picture will show and tell who won.
With a processor performing 3MIPS, it is
not easy to make a video signal in software. Each
instruction performed takes 1/3 us. Each scan-line on
the screen takes 64us, where 52us are visible, so it
gives 52*3=156 visible clock cycles per line. Maximum
resolution that can be obtained is 156 pixels in x-axis
if the software is setting one pixel per clock (using
for example only bcf and bsf), but more is needed to
make a game, like loops and such. A loop quantifies the
time to 3-clock pieces, giving a resolution of 52
pixels. (One could obtain a kind of 156pixels resolution
with one or two offset nops, but the code to select this
would eat to many clock cycles to do any good). However
Tetris is quite simple, the resoluton is quite low, and
there is no motion, the blocks of pixels are just turned
on and off. The most demanding part of the game is to
show the score at the bottom of the screen, it is shown
in the bottom of the screen. It obtains higher
resolution by loading the PORTB with the bitmap for the
number and shift it out one pixel per clock cycle.
So far I've only talked about the
graphic generation. But there is more to it to get a
video signal. All scan-lines first have a 4us-sync
pulse, then black for 8us, then the 52us graphic comes.
These horizontal sync-pulses makes the TV understand
when a scan-line starts, but there is needed to send
information about when a new picture starts too, it is
called vertical sync, and is a special pattern that
tells the TV that a new image is coming. There are two
kinds of vertical sync, because the image is divided
into two part images, showing even and odd lines, to get
less flickering. In Tetris, the two images are
identical, so the game is not using the full
y-resolution possible, but it doesn't matter because it
is way better than the x-resolution anyway, making the
x-resolution the biggest problem.
The game-field is kept in memory as a
32byte array, 16x16 bits, where one bit is one
pixel-block on the screen. The area to the upper left is
for showing the next block, and by making it a part of
the game field it is possible to use the same
block-drawing routines as for the game, and thereby
saving memory. Each frame, the falling block is first
removed from the game-field, and then tests are
performed if the block can move, as the player wants it
to. Then the block is drawn back to the screen at the
new position. When a block is to be tested, put or
removed, it first must be generated. To generate a block
means compressing it from the compressed data, rotating
it and then store the relative coordinates of the block
in the block array. The block data is compressed in
relative coordinates. In compressed format, each
coordinate is stored in two bits for both x and y, where
the two bits can represent the numbers �1,0,1,2. These
values need to be uncompressed to 4*2 byte sized values
representing the coordinates in two�s complement format.
Depending of the angle the block should have, the
coordinates might need to be mirrored or/and swapped.
When the block have been created it can easily be put,
removed or tested. The test routine checks if there is
any pixels set on the block positions where the block
should be put. If pixels are set, then the block can�t
be put there. New blocks are selected at random, where
the random number is a counter that increases for every
frame, making the random number dependent of how long it
takes for the player to place the block, making a quite
good random number.
The game stuff, like checking joystick
and move stuff around, is taken care of in the first
scan-lines, when no graphics is drawn. During the time
before the play-field is shown, there is a little bit of
free time to play the music, but there is not time to
play it on all lines, and that make the music sound
distorted. The music is stored in the data eeprom, and
stored in a compressed one byte format, where one byte
contains length and note. The note's frequency is looked
up in a table, and so is the length too. (The
frequencies are based on the line frequency so they are
not exactly the correct frequencies) The speed of the
game is increasing constantly and music-speed increases
as the game speed increases.
Making this kind of software is mostly a
clock-cycle-counting project, all timings are quite
critical, so whatever paths the execution-flow of the
program takes, it must take the same number of clock
cycles. This is quite hard, and I've not managed to do
this on all lines, so the image is a little bit bent in
some places. (Most analog TV-sets fix this, but on some
digital projectors it is more visible)
The hardware is quite simple because
everything is made in software. Two resistors, forming a
DA converter together with the input impedance of the
TV, generate the video signal. This can generate the
levels 0v (sync), 0.3v (black), 0.7v (gray), and 1.0v
(white). To be able to handle the variation of input
resistance of different audio equipment, two resistors
are used to make a 1-bit DA to generate the audio. When
generating the video, the PORTB is used as a shift
register to get one pixel per instruction when
high-resolution text is shown on the screen. Shifting a
port requires the port to be set as output if a whole
byte is to be shifted out. First, this seems like a
problem, the whole port can't be used for anything else
than video generation, but that is not quite correct. A
port can be used as an input when not used as a shift
register, so in Tetris PORTB it is used for joystick
input when not used as a shift register. The digital
joystick is a switch to ground, so all needed to connect
it to the PIC is a couple of pull up resistors, and that
is available inside the PIC. Unfortunately it is not
that simple, if a pin on a port is grounded when used as
an output, the output buffer of the pic would burn up,
so this is solved by adding one extra 1k resistor on
each pin to limit the current. What about those pull up
resistors? There are 10k pull up resistors built into
the PIC that can be switched on and off. However, using
them would be a too strong pull up, so the 1k current
limiting resistor (plus bad switches in the joystick)
can't pull the input low enough. Therefor an external
100k resistor pull up network is added. The power supply
part of the circuit is quite simple, it uses a standard
7805 to get a 5v supply. The input can be 8-18 volt, DC
or AC (Thanks to the diode at the power input)
Tetris Game Schematic |