Draw a Cylon eye in DOS
I first got “into” programming because it was fun. And while I also write programs to solve problems and make useful tools, that desire to find fun things to do when programming has stuck with me to this day.
I wanted to share a fun program you can write for yourself. If you remember the original Battlestar Galactica TV show from the late 1970s, or the rebooted show from the early 2000s, you may recall the Cylons. (If you don’t know the show, it was a low-budget sci-fi TV series that came out right after the first Star Wars movie.) The “combat” Cylon had a distinguishing feature: its sweeping red eye.
Let’s write a program to emulate the sweeping “eye” of a Cylon:
Using DOS conio
I wrote this program under FreeDOS because DOS makes it really easy to draw extended characters to the screen, beyond the 7-bit ASCII characters. The DOS “extended ASCII” character set includes a filled-in block (0xdb
) and a series of partially “shaded” boxes (0xb0
, 0xb1
, and 0xb2
). We can use these extended characters to draw an eye (filled-in block) and simulate a “trail” behind the movement (with the shaded boxes).
I’ll use the OpenWatcom C compiler on FreeDOS, which includes a few useful libraries. The conio.h
library provides direct access to the video console, and the graph.h
functions let us set the text colors and define regions on the screen called text “windows.”
To write this program, we’ll need only a few functions:
_setvideomode
clears the screen and puts the display into a specific mode, like 80×25 color_settextwindow
defines a region of the display as a text “window”_settextposition
will move the cursor to a row,col coordinate on the screen_outmem
will print characters from an array to the console
Drawing an “eye”
We can use these functions to draw an “eye” that moves from left to right across the screen. First, define an array of five elements, called right
. The array should contain a space, plus the shaded boxes (light to dark), then the filled-in block.
char right[] = { 0x20, 0xb0, 0xb1, 0xb2, 0xdb };
The 0x20
character is the ASCII space; the 0xdb
character is a filled-in block. The middle characters are shaded boxes.
To “animate” this block moving across the screen, draw successive iterations of the array from columns 1 to 75. The leading space in the array effectively erases the previous iteration of the “eye.”
for (col = 1; col <= 75; col++) {
_settextposition(1, col);
_outmem(right, 5);
delay(10); /* ms */
}
This is a short loop that contains only a few statements: for each value from 1 to 75, set the cursor position to a specific column on line 1, then print the 5 elements from the right
array. The delay
function (defined in i86.h
) delays program execution for a specified number of milliseconds. In this case, a very short delay of 10ms is fast enough to look like the “eye” is moving but slow enough that a human can see it.
We can also write a similar loop to move the “eye” from the right to left, using another array called left
. If we put both loops together, we have a function that continuously draws an eye moving to the right, then to the left, and back again.
void cylon(void)
{
char left[] = { 0xdb, 0xb2, 0xb1, 0xb0, 0x20 };
char right[] = { 0x20, 0xb0, 0xb1, 0xb2, 0xdb };
short col;
while (1) {
for (col = 1; col <= 75; col++) {
_settextposition(1, col);
_outmem(right, 5);
delay(10); /* ms */
if (kbhit()) {
return;
}
}
for (col = 75; col >= 1; col--) {
_settextposition(1, col);
_outmem(left, 5);
delay(10); /* ms */
if (kbhit()) {
return;
}
}
}
}
Because the function runs forever, I’ve added the kbhit
test to each iteration of both loops. This returns a true value only if the user has pressed a key on the keyboard. Later, you can retrieve that key value using the getch
function. But for this function, I’m only using kbhit
to detect when the user is ready to end the program.
The ‘main’ program
We can use this function as the core part of our program to draw the Cylon “eye.” The main
program only needs to set up the screen, then call the cylon
function to draw the sweeping eye. When the cylon
function returns, the program can clear the keyboard input buffer using getch
, then exit back to DOS.
In OpenWatcom, the _setvideomode
function sets a specific video mode, like _TEXTC80
for color text at 80 columns and 25 lines. The function returns the number of lines, or zero if there was an error.
After that, we can use _settextwindow
to define a region on the screen as a text “window,” and _clearscreen
to erase either the whole screen (with _GCLEARSCREEN
) or just the window (with _GWINDOW
). If we set the background color with _setbkcolor
before clearing, we can effectively set a background color across the full screen or the window.
/* clear screen */
if (_setvideomode(_TEXTC80) == 0) {
puts("cannot set video mode");
return 1;
}
/* draw the eye */
_setbkcolor(7); /* white */
_clearscreen(_GCLEARSCREEN);
_setbkcolor(0); /* black */
_settextwindow(10, 1, 10, 80); /* from r,c=10,1 to r,c=10,80 */
_clearscreen(_GWINDOW);
_settextcolor(12); /* br red */
_settextcursor(0x2000); /* no cursor */
Putting it all together
We can assemble these pieces into a DOS program that erases the screen with a white background, then defines a black 1-line “window” across the width of the screen on line 10. This is an attempt to emulate the Cylons from the TV show, which were actually shiny silver with a black visor that contained the bright red “eye” that swept ominously from side to side.
It’s important to know that after you’ve defined a text window, the _settextposition
function uses coordinates relative to the window. So drawing on “line 1” in the window just prints text on the first line of the window.
#include <stdio.h>
#include <conio.h> /* console */
#include <graph.h> /* colors */
#include <i86.h> /* delay */
void cylon(void)
{
char left[] = { 0xdb, 0xb2, 0xb1, 0xb0, 0x20 };
char right[] = { 0x20, 0xb0, 0xb1, 0xb2, 0xdb };
short col;
/* text window already defined, so use "row" = 1 */
while (1) {
for (col = 1; col <= 75; col++) {
_settextposition(1, col);
_outmem(right, 5);
delay(10); /* ms */
if (kbhit()) {
return;
}
}
for (col = 75; col >= 1; col--) {
_settextposition(1, col);
_outmem(left, 5);
delay(10); /* ms */
if (kbhit()) {
return;
}
}
}
}
int main()
{
/* clear screen */
if (_setvideomode(_TEXTC80) == 0) {
puts("cannot set video mode");
return 1;
}
/* draw the eye */
_setbkcolor(7); /* white */
_clearscreen(_GCLEARSCREEN);
_setbkcolor(0); /* black */
_settextwindow(10, 1, 10, 80); /* from r,c=10,1 to r,c=10,80 */
_clearscreen(_GWINDOW);
_settextcolor(12); /* br red */
_settextcursor(0x2000); /* no cursor */
cylon();
/* done */
if (getch() == 0) { getch(); }
_setvideomode(_DEFAULTMODE);
return 0;
}
Save this as cylon.c
on your FreeDOS system, and compile it with OpenWatcom (WCL
is the Watcom Compiler and Linker) like this:
> WCL cylon.c
Now you can run the cylon.exe
program whenever you want to experience the roving red eye of a Cylon moving from left to right…
And from right to left…