{"id":9484,"date":"2025-02-05T03:00:00","date_gmt":"2025-02-05T08:00:00","guid":{"rendered":"https:\/\/www.both.org\/?p=9484"},"modified":"2025-02-01T14:41:21","modified_gmt":"2025-02-01T19:41:21","slug":"draw-a-cylon-eye-in-dos","status":"publish","type":"post","link":"https:\/\/www.both.org\/?p=9484","title":{"rendered":"Draw a Cylon eye in DOS"},"content":{"rendered":"<div class=\"pld-like-dislike-wrap pld-template-1\">\r\n    <div class=\"pld-like-wrap  pld-common-wrap\">\r\n    <a href=\"javascript:void(0)\" class=\"pld-like-trigger pld-like-dislike-trigger  \" title=\"\" data-post-id=\"9484\" data-trigger-type=\"like\" data-restriction=\"cookie\" data-already-liked=\"0\">\r\n                        <i class=\"fas fa-thumbs-up\"><\/i>\r\n                <\/a>\r\n    <span class=\"pld-like-count-wrap pld-count-wrap\">1    <\/span>\r\n<\/div><\/div>\n<p>I first got \u201cinto\u201d 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.<\/p>\n\n\n\n<p>I wanted to share a fun program you can write for yourself. If you remember the original <em>Battlestar Galactica<\/em> TV show from the late 1970s, or the rebooted show from the early 2000s, you may recall the Cylons. (If you don\u2019t know the show, it was a low-budget sci-fi TV series that came out right after the first <em>Star Wars<\/em> movie.) The \u201ccombat\u201d Cylon had a distinguishing feature: its sweeping red eye.<\/p>\n\n\n\n<p>Let\u2019s write a program to emulate the sweeping \u201ceye\u201d of a Cylon:<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"using-dos-conio\">Using DOS conio<\/h2>\n\n\n\n<p>I wrote this program under FreeDOS because DOS makes it really easy to draw <em>extended<\/em> characters to the screen, beyond the 7-bit ASCII characters. The DOS \u201cextended ASCII\u201d character set includes a filled-in block (<code>0xdb<\/code>) and a series of partially \u201cshaded\u201d boxes (<code>0xb0<\/code>, <code>0xb1<\/code>, and <code>0xb2<\/code>). We can use these extended characters to draw an eye (filled-in block) and simulate a \u201ctrail\u201d behind the movement (with the shaded boxes).<\/p>\n\n\n\n<p>I\u2019ll use the OpenWatcom C compiler on FreeDOS, which includes a few useful libraries. The <code>conio.h<\/code> library provides direct access to the video <em>console<\/em>, and the <code>graph.h<\/code> functions let us set the text colors and define regions on the screen called text \u201cwindows.\u201d<\/p>\n\n\n\n<p>To write this program, we\u2019ll need only a few functions:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><code>_setvideomode<\/code> clears the screen and puts the display into a specific mode, like 80&#215;25 color<\/li>\n\n\n\n<li><code>_settextwindow<\/code> defines a region of the display as a text \u201cwindow\u201d<\/li>\n\n\n\n<li><code>_settextposition<\/code> will move the cursor to a <em>row,col<\/em> coordinate on the screen<\/li>\n\n\n\n<li><code>_outmem<\/code> will print characters from an array to the console<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"drawing-an-eye\">Drawing an \u201ceye\u201d<\/h2>\n\n\n\n<p>We can use these functions to draw an \u201ceye\u201d that moves from left to right across the screen. First, define an array of five elements, called <code>right<\/code>. The array should contain a space, plus the shaded boxes (light to dark), then the filled-in block.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>    char right&#91;] = { 0x20, 0xb0, 0xb1, 0xb2, 0xdb };<\/code><\/pre>\n\n\n\n<p>The <code>0x20<\/code> character is the ASCII space; the <code>0xdb<\/code> character is a filled-in block. The middle characters are shaded boxes.<\/p>\n\n\n\n<p>To \u201canimate\u201d this block moving across the screen, draw successive iterations of the array from columns 1 to 75. The leading space in the array effectively <em>erases<\/em> the previous iteration of the \u201ceye.\u201d<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>    for (col = 1; col &lt;= 75; col++) {\n        _settextposition(1, col);\n        _outmem(right, 5);\n        delay(10);                 \/* ms *\/\n    }<\/code><\/pre>\n\n\n\n<p>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 <code>right<\/code> array. The <code>delay<\/code> function (defined in <code>i86.h<\/code>) 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 \u201ceye\u201d is moving but slow enough that a human can see it.<\/p>\n\n\n\n<p>We can also write a similar loop to move the \u201ceye\u201d from the right to left, using another array called <code>left<\/code>. 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.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>void cylon(void)\n{\n    char left&#91;] = { 0xdb, 0xb2, 0xb1, 0xb0, 0x20 };\n    char right&#91;] = { 0x20, 0xb0, 0xb1, 0xb2, 0xdb };\n    short col;\n\n    while (1) {\n        for (col = 1; col &lt;= 75; col++) {\n            _settextposition(1, col);\n            _outmem(right, 5);\n            delay(10);                 \/* ms *\/\n\n            if (kbhit()) {\n                return;\n            }\n        }\n\n        for (col = 75; col &gt;= 1; col--) {\n            _settextposition(1, col);\n            _outmem(left, 5);\n            delay(10);                 \/* ms *\/\n\n            if (kbhit()) {\n                return;\n            }\n        }\n    }\n}<\/code><\/pre>\n\n\n\n<p>Because the function runs forever, I\u2019ve added the <code>kbhit<\/code> 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 <code>getch<\/code> function. But for this function, I\u2019m only using <code>kbhit<\/code> to detect when the user is ready to end the program.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"the-main-program\">The \u2018main\u2019 program<\/h2>\n\n\n\n<p>We can use this function as the core part of our program to draw the Cylon \u201ceye.\u201d The <code>main<\/code> program only needs to set up the screen, then call the <code>cylon<\/code> function to draw the sweeping eye. When the <code>cylon<\/code> function returns, the program can clear the keyboard input buffer using <code>getch<\/code>, then exit back to DOS.<\/p>\n\n\n\n<p>In OpenWatcom, the <code>_setvideomode<\/code> function sets a specific video mode, like <code>_TEXTC80<\/code> for color text at 80 columns and 25 lines. The function returns the number of lines, or zero if there was an error.<\/p>\n\n\n\n<p>After that, we can use <code>_settextwindow<\/code> to define a region on the screen as a text \u201cwindow,\u201d and <code>_clearscreen<\/code> to erase either the whole screen (with <code>_GCLEARSCREEN<\/code>) or just the window (with <code>_GWINDOW<\/code>). If we set the background color with <code>_setbkcolor<\/code> before clearing, we can effectively set a background color across the full screen or the window.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>    \/* clear screen *\/\n\n    if (_setvideomode(_TEXTC80) == 0) {\n        puts(\"cannot set video mode\");\n        return 1;\n    }\n\n    \/* draw the eye *\/\n\n    _setbkcolor(7);                    \/* white *\/\n    _clearscreen(_GCLEARSCREEN);\n\n    _setbkcolor(0);                    \/* black *\/\n    _settextwindow(10, 1, 10, 80);     \/* from r,c=10,1 to r,c=10,80 *\/\n    _clearscreen(_GWINDOW);\n\n    _settextcolor(12);                 \/* br red *\/\n    _settextcursor(0x2000);            \/* no cursor *\/<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"putting-it-all-together\">Putting it all together<\/h2>\n\n\n\n<p>We can assemble these pieces into a DOS program that erases the screen with a white background, then defines a black 1-line \u201cwindow\u201d 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 \u201ceye\u201d that swept ominously from side to side.<\/p>\n\n\n\n<p>It\u2019s important to know that after you\u2019ve defined a text window, the <code>_settextposition<\/code> function uses coordinates <em>relative to the window<\/em>. So drawing on \u201cline 1\u201d in the window just prints text on the first line of the window.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>#include &lt;stdio.h&gt;\n#include &lt;conio.h&gt;                     \/* console *\/\n#include &lt;graph.h&gt;                     \/* colors *\/\n#include &lt;i86.h&gt;                       \/* delay *\/\n\nvoid cylon(void)\n{\n    char left&#91;] = { 0xdb, 0xb2, 0xb1, 0xb0, 0x20 };\n    char right&#91;] = { 0x20, 0xb0, 0xb1, 0xb2, 0xdb };\n    short col;\n\n    \/* text window already defined, so use \"row\" = 1 *\/\n\n    while (1) {\n        for (col = 1; col &lt;= 75; col++) {\n            _settextposition(1, col);\n            _outmem(right, 5);\n            delay(10);                 \/* ms *\/\n\n            if (kbhit()) {\n                return;\n            }\n        }\n\n        for (col = 75; col &gt;= 1; col--) {\n            _settextposition(1, col);\n            _outmem(left, 5);\n            delay(10);                 \/* ms *\/\n\n            if (kbhit()) {\n                return;\n            }\n        }\n    }\n}\n\nint main()\n{\n    \/* clear screen *\/\n\n    if (_setvideomode(_TEXTC80) == 0) {\n        puts(\"cannot set video mode\");\n        return 1;\n    }\n\n    \/* draw the eye *\/\n\n    _setbkcolor(7);                    \/* white *\/\n    _clearscreen(_GCLEARSCREEN);\n\n    _setbkcolor(0);                    \/* black *\/\n    _settextwindow(10, 1, 10, 80);     \/* from r,c=10,1 to r,c=10,80 *\/\n    _clearscreen(_GWINDOW);\n\n    _settextcolor(12);                 \/* br red *\/\n    _settextcursor(0x2000);            \/* no cursor *\/\n\n    cylon();\n\n    \/* done *\/\n\n    if (getch() == 0) { getch(); }\n\n    _setvideomode(_DEFAULTMODE);\n    return 0;\n}\n<\/code><\/pre>\n\n\n\n<p>Save this as <code>cylon.c<\/code> on your FreeDOS system, and compile it with OpenWatcom (<code>WCL<\/code> is the <strong>W<\/strong>atcom <strong>C<\/strong>ompiler and <strong>L<\/strong>inker) like this:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>&gt; WCL cylon.c<\/code><\/pre>\n\n\n\n<p>Now you can run the <code>cylon.exe<\/code> program whenever you want to experience the roving red eye of a Cylon moving from left to right\u2026<\/p>\n\n\n\n<figure class=\"wp-block-image aligncenter size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"533\" height=\"400\" src=\"https:\/\/www.both.org\/wp-content\/uploads\/2025\/02\/cylon1.png\" alt=\"\" class=\"wp-image-9485\"\/><figcaption class=\"wp-element-caption\">Cylon eye moving left to right<\/figcaption><\/figure>\n\n\n\n<p>And from right to left\u2026<\/p>\n\n\n\n<figure class=\"wp-block-image aligncenter size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"533\" height=\"400\" src=\"https:\/\/www.both.org\/wp-content\/uploads\/2025\/02\/cylon2.png\" alt=\"\" class=\"wp-image-9486\"\/><figcaption class=\"wp-element-caption\">Cylon eye moving right to left<\/figcaption><\/figure>\n","protected":false},"excerpt":{"rendered":"<p>Have fun by writing a program to simulate a Cylon&#8217;s sweeping red eye<\/p>\n","protected":false},"author":33,"featured_media":2725,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_lmt_disableupdate":"","_lmt_disable":"","footnotes":""},"categories":[340,150],"tags":[267,147,152],"class_list":["post-9484","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-freedos","category-programming","tag-freedos","tag-fun","tag-programming"],"modified_by":"Jim Hall","_links":{"self":[{"href":"https:\/\/www.both.org\/index.php?rest_route=\/wp\/v2\/posts\/9484","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.both.org\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.both.org\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.both.org\/index.php?rest_route=\/wp\/v2\/users\/33"}],"replies":[{"embeddable":true,"href":"https:\/\/www.both.org\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=9484"}],"version-history":[{"count":1,"href":"https:\/\/www.both.org\/index.php?rest_route=\/wp\/v2\/posts\/9484\/revisions"}],"predecessor-version":[{"id":9487,"href":"https:\/\/www.both.org\/index.php?rest_route=\/wp\/v2\/posts\/9484\/revisions\/9487"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.both.org\/index.php?rest_route=\/wp\/v2\/media\/2725"}],"wp:attachment":[{"href":"https:\/\/www.both.org\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=9484"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.both.org\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=9484"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.both.org\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=9484"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}