2 years ago 2 years ago C Share

How to Compile C Programs for DOS in 2021


Difficulty
Interest
Education
Overall

This page explains the steps I used to re-compile a C program I wrote on a DOS PC in 1997, but using a modern PC in 2021.

I wanted to do this so I could improve the online playability of my game called "Eel", especially on devices with no physical keyboard (like phones and tablets).

It only involved making a small change to the source code (just the numbers for the keycodes for which keys are pressed to play the game), and a change to the in-game rotating instruction menu text to reflect this. (It used to say "Arrow keys to turn", and now it says "Press IJKL to turn".)

To make this small change, though, I needed to re-compile the game, which meant I had to have a C compiler.

The C compiler I used (which is a more recent version of the same one that I originally made Eel with, is DJGPP.

To compile the game to work on DOS, so that it would run in the online emulator, and with only those minimal changes to the code, meant that I had to somehow install a C development environment that can compile for vintage DOS programs.

To make this small change to the game ended up taking longer (about 8 hours just to do that) than the entire process of getting from "I wonder if I can run an old DOS game in a web browser on my website?" to having a fully-working version of my original .exe file of the game, as seen here on this website (which took about 5 hours). That first version used the original .exe which needed the arrow keys to play it. The emulated popup keyboard for the js-dos DOS emulator doesn't have arrow keys on it, so that was a problem on phones and tablets. Also, even when playing in in the website window, with a real keyboad, the arrow keys were still read as such by the browser, causing the web page (including the embedded game window) to scroll one line up or down whenever you pressed the up or down arrow (like they normally do when reading a web page).

So I thought I'd have a go at fixing that.

Currently this page is a quick draft, so I can write out the steps I had to take (since there were many), while I can still remember them, and while most of the browser tabs I used to learn how to do it are still open. I did close one or two Firefox windows, though I still have 67 tabs open right now, and most of those relate somehow to this task.

The PC emulation for my game "Eel" is done using js-dos.

The "DOS Zone Studio" here allowed me to create a .jsdos module file from my original eel.exe DOS program, you can use it to make a runnable DOS disk from any actual PC files or games you may have from that era.

Installing DOS 6.22 on VirtualBox

Since I've already got VirtualBox installed, and have got used to it, this seemed like it would be the easiest way to do it. VirtualBox lets you run entire other operating systems within an already-running operating system (even Windows 10 can run within Windows 10).

I've heard that VirtualBox doesn't work on the latest versions of Mac OS, though they will probably update VirtualBox sometime so that it does. If you have a Mac this will probably work better in the free version of VMWare (another virtualisation program similar to VirtualBox). I've heard that the free version of VMWare doesn't support sound in DOS emulation (and that the paid versions do), though you don't need the sound to actually work to compile programs.

I'll write another page later about how to set up VirtualBox.

I followed the instructions here on how to install DOS in VirtualBox (skipping the first step of installing VirtualBox).

https://www.instructables.com/How-To-Install-DOS-622-Under-VirtualBox/

I found this link (and the others on this page) from Google searches, e.g. "how to install dos on virtualbox".

I did few minor things differently (so that they worked better, or at all) to the above page, such as:

In the "Settings" for the virtual machine, which I named DOS 6.22, this is what I have:

Under the System/Acceleration tab, "Enable Nested Paging" I turned off (Like the Instructables web page said to). I left the "Paravirtualization Interface set to the default setting of "Default" rather than turning it off, and this works fine.

Display: I gave it 16 MB rather than the recommended 8 MB, since why not (that's not a lot of RAM for me to spare, my physical PC has 32 GB of it). I think all the other Display settings are like they were originally, which is NO 3D Acceleration, and VBoxVGA as the controller.

I just realised that I probably should have tried to experiment with the "Shared Folders" setting, which may have saved me a lot of hassle. It really was a lot of hassle swapping files between the virtual DOS PC and my real PC.

In the Storage setting, I have the standard disk which was made as part of making the VM (VM means Virtual Machine from now on, on this web page). And also I made another disk which I called "SharingImage.vdi" which I thought might help share the files between the DOS installation and my actual Windows 10 PC. I didn't end up needing it though.

I also made the DOS VM window bigger and nicer to read by going into the "View" menu at the top of the running DOS VM window, and "Virtual Screen 1", and "Resize to 200%".

I found the (lack of) mouse integration annoying, and when the mouse disappeared from my main Windows OS, I had to press Ctrl-F (using the RIGHT side Ctrl key, not the one on the left side of the keyboard) and then wait a couple of seconds, and then press it again. Then the mouse pointer would return. If I was careful not to click the mouse inside the DOS VM window at all, this problem of the mouse cursor disappearing didn't happen.

DOS itself installed very, very fast on the modern PC in the VM. Compared to the 1990s when it took many hours just to install DOS, the actual installation process of DOS itself (once the VM was created) took about 3 minutes. And involved changing the floppy disk in the menu in the running VB screen each time the DOS installer asked to "insert the next floppy disk". I had downloaded three floppy disk image files (.img) which were what I used to install DOS itself from. The first one of these is a bootable floppy disk image, which the VM boots into when it has no OS (such as DOS) installed yet.

Installing the DJGPP C Compiler on DOS 6.22 in VirtualBox

This turned out to be quite difficult. The main problems being:

  • How to get files downloaded from the internet onto the DOS VM (VM means "Virtual Machine"). DOS does not have internet access, nor (without also installing Windows on the same VM) any type of web browser. There may be a way to get a vintage text-only one, though this seemed like it would be a lot of effort.
  • It seemed easier to download the C compiler from the internet using my main OS, and then copy the files to the DOS VM. I had to think about how to do this, and it may have been easier to make a shareable folder. Though even that isn't as easy as it sounds, as the folder (and hard drive partition) that is shared would have to be in a format that DOS 6.22 can read, and not in a modern format like NTFS. Plain DOS can't even read FAT32 (I think), only the original FAT type of filesystem, which is also known as FAT16.
  • There were issues with "long file names", since DOS can only handle file names of 8 characters maxiumum, followed by a . and then three more characters for the file extension. The way I got around this issue was to copy the .zip files of DJGPP (the C compiler and other related files) to the DOS VM while they were still in .zip form, and then unzup them in the DOS VM itself, using the version of unzip called unzip32.exe which you can get from the DJGPP website. This worked.

It was quite a long process figuring out exactly what to download for DJGPP, and then what to do with it once I got it. There isn't one "install" file, you need to do it manually (I'm fairly sure, at least, if there is an easier way, I didn't find one).

I used the page here to select which .zip files to download for DJGPP:

http://www.delorie.com/djgpp/zip-picker.html

I picked DOS as the system, and I got RHIDE (which I found really helped a lot when I later had a segmentation fault error in my new version of EEL.EXE), and also I got the Allegro and the GRX for sound and graphics, though I'm not sure if I really needed them or not.

You also need the unzip32.exe program, which you can get from here:

http://www.delorie.com/djgpp/dl/ofc/

Then you have to get the files from wherever you downloaded them to onto your DOS VM. This was also quite hard.

What I ended up doing (there may be a better and quicker way than this), was adding the VirtualBox file for the entire DOS root C drive to my VirtualBox Kali Linux VM also. To do this you need to have both VMs (DOS and Kali Linux) not running (i.e. shut down). You can probably use any other VM you might have for this, as long as that VM can read and write to the DOS-formatted virtual hard drive, and the other (non-DOS) VM can also share files with your main PC.

Kali was able to do this, so when I booted into Kali (and the DOS VM has to not be running when you run Kali now, or you will get an error saying the disk is already in use by another running VM), the DOS VM's main hard disk was now visible on the Kali Linux desktop, and you could mount it and open it, and copy files to and from it.

I used Firefox in Kali to download DJGPP, though I could have done it on Windows also.

I don't have any shared folders set up between Windows and Kali (though this would be a lot easier I think to set up than to share folders between my physically running (non-VM) Windows 10 and DOS 6.22, without making a special FAT16 partition on my main Windows machine.

So to swap files between Windows 10 and Kali, I used a removable (real, physical, not virtual) USB flash "stick" drive. My Kali is set to have USB 3 enabled, and when I put a USB flash "stick" drive into the USB port of my PC, and Kali is running, it will (usually) appear in Kali on the desktop, and can be mounted.

Just to get the DJGPP C compiler working, "all" I had to do was download all the .zip files which the DJGPP Zip Picker recommended for me, and then copy them into the DOS hard drive while it was also mounted in Kali Linux.

If I had known that to start with, it would have saved a lot of time. Since I unzipped all the files in Kali (thinking that would be quicker than doing it in command-line plain DOS) first, the first time. Do not do this! Then, when I copied those files to the DOS VM, I got a bunch of weird errors, which I realised was because of the long filenames not being handled properly.

Evidently, if you unzip the various DJGPP .zip files within the DOS VM itself, using upzip32.exe, the long filenames will be changed to short ones in a different way (and one that works) to if you unzip them on an OS that supports long filenames (meaning anything longer than the DOS 8.3 character limit).

Save all the DJGPP .zip files into a folder called DJGPP in the root of your DOS emulated hard disk.

I also copied the source and .exe files for my Eel game onto the DOS disk.

Then I shut down the Kali Linux VM, so the DOS VM can be booted up, using the same emulated hard disk that you just copied the files to.

In the DOS VM

Once you have copied the DJGPP files to your DOS VM's virtual hard disk, shut down the other (e.g. Kali Linux) VM, and start up the DOS one. The files should be there.

Then you can unzip them.

I found it was possible to do this in one command (though I didn't realise it for a while).

I think it was just as simple as UNZIP32 *.ZIP, while your'e "in" the DJGPP directory in your running DOS 6.22 VM.

There are many files to be unzipped (hundreds of them) and it takes a couple of minutes, even on a modern PC. It probably took an hour or more in the 1990s just for the unzipping to happen.

Once they are unzipped, you need to add a couple of lines to your CONFIG.SYS and AUTOEXEC.BAT files, and then you're good to go with compiling C programs for DOS, using DOS.

The instructions for this are on the "result" page of the DJGPP Zip Picker, and are copied and pasted here:

Modifying the DOS config files

Note that DOS mostly doesn't care about whether you write things in upper or lower case letters, though very occasionally when using some programs, it does care.

You need to update your C:\CONFIG.SYS to include the following lines (edit the first to suit your installation, and if these lines already exist, it's OK if they have larger numbers than these examples):

shell=c:\dos\command.com c:\dos /e:2048 /p  files=40  fcbs=40,0  

You need to update your C:\AUTOEXEC.BAT to include the following lines:

set PATH=C:\DJGPP\BIN;%PATH%
set DJGPP=C:\DJGPP\DJGPP.ENV  

Note that the PATH statement should follow any other PATH statements, or you may edit an existing PATH statement.

[What I actually did was "edit an existing PATH statement" which now looks like this, and not like the example above. I did add the other set DJGPP= line just like in the example above]:

PATH C:\DOS;C:\DJGPP\BIN

You'll need to reboot your computer for these changes to take effect.

Editing and Compiling a C Program with DJGPP in DOS

Make a folder (I used DJGPP\HOME though I think you can probably use any folder, maybe) to put your C source files in.

I copied my .C source code for Eel into here.

The compiler can be ran by:

GCC -O EEL.EXE EEL.C 

Though I was using different version numbers of my files (so I could backtrack any mistakes I made), and also the -G option puts debugging information into the output file, which I found to be really helpful when I had a bug in my new version of Eel. For example:

GCC -O EEL29.EXE EEL29.C 

I did a test compile of my original EEL.C source to see if it would work, before changing anything. To get this to work was quite hard.

I had to make some changes to the .C source to run in the new version of DJGPP (I did the original with v1 of DJGPP and a few things are different, evidently).

I used the standard DOS command called EDIT to edit my .C file most of the time. A couple of times I used the "IDE" editor in RHIDE also.

I should install mouse drivers on my DOS VM sometime, if I end up using it much I will definitely do that. And a better shell than the default COMMAND.COM which is really basic, and doesn't even have things like a history of previous commands (like when you press the up arrow in basically every modern command prompt window in any modern OS), or filename completion when you press TAB, so in plain DOS you end up having to type things over and over and over.

One was to add #include <stdlib.h> to the headers (the 5th line of my new source code as shown below). This was needed for the rand() function in C, it must have not been needed by the first version of DJGPP when I did it in 1997.

After that header was included, I found there is a function already in C called random(), and my own program defined its own random() function also, which clashed. So I edited my .C source so that all the mentions of my own custom random() function were changed to getrandom(), a name I made up, thinking (correctly) there wouldn't already be a C function called that.

Then I was able to compile the game into an .exe, and run it in the DOS VM by typing

EEL26 

(Or whatever version number I was on at the time). I had previously tested my original EEL.EXE and it ran perfectly in the VM, except there was no sound. But the lack of sound was because (I think) there are no Sound Blaster drivers installed in my DOS VM installation (yet). I don't need them for this so I didn't bother installing them. They are only needed to play the game with sound in the VM, not to compile it (and then run it on this website like here).

Then, I got a mysterious segmentation fault error, which would crash the game right after it played the opening song.

A segmentation fault error means the program is trying to write to memory that it's not supposed to be writing to. This can happen a few ways in C, for example if you try to access and array element with an index that's so much larger than the actual size of the array that the resulting memory where the hypothetical array element would be, if it existed, is outside the memory allocated to your program.

I used the debugger RHIDE, to figure out that the error was happening in my original C random number function. Which I should have really thought of before I did, since there was a change needed to the headers for the random number function. And if I was paying more attention to that, I could have found the bug faster, since the bug was the way my original C program chose random numbers.

I'll write more later about how I used RHIDE to find that this was where the segmentation fault was occurring.

This was my original random number function:

int random (int range)
{
    int value;
    value = rand() * range / 65535;
    return value;
}

And the problem was with the hard-coded number 65535, which assumes that's the biggest number that the C function rand() is going to return. This must have changed since the earlier version of the DJGPP C compiler I used in 1997.

I tried to substitute the constant RAND_MAX instead of the 65535 here, and that was a partial improvement. The game didn't crash completely now, but it still did weird things and hung sometimes. It seemed the problem was still with the random number picking.

This was what I ended up with, which worked beautifully:

int getrandom (int range)
{
    int value;
 /* value = rand() * range / 65535; This caused a segmentation fault with the newer C compiler! */
    value = rand() % range;
    return value;
}

It uses a modulo (the % operator as seen above) so that it doesn't matter what the largest possible random number that rand() returns is. I left in the original version as a comment, just so I could see the difference.

Modifying Eel to Use Different Keys

Having got this far (that is, to be able to re-compile my original un-modified C program from 1997, in DOS, to run on DOS, in 2021), the rest was quite easy.

I had to look up the keycodes for the I, J, K, and L keys, and change the numbers in the first few lines of the source code so that these would be read instead of the codes for the arrow keys.

I left the original codes in as comments, which you can also see below.

Then all I had to do was change the instruction line, in the source, which used to say "ARROW KEYS TO TURN", to "PRESS IJKL TO TURN". I chose the same number of characters in each phrase so I didn't have to think about how it would be centered on the screen in the game.

Then I compiled it again and that's the version of Eel that you can now play on this website.

There was one extra change, which I discovered when making the .jsdos module file to run in the web page emulator. Which is that I needed to also include the file CWSDPMI.EXE in the .zip which made for the DOS Zone Studio to load. This file CWSDPMI.EXE was found in the DJGPP\BIN folder of the DJGPP installation.

C Source Code of Eel

I've left the source code on this page also so you can see the changes made:

#include <stdio.h>
#include <conio.h>
#include <dos.h>
#include <pc.h>
#include <stdlib.h>

#define UP           105    /* = I, and 328 was up    arrow key */
#define DOWN         107    /* = K, and 336 was down  arrow key */
#define LEFT         106    /* = J, and 331 was left  arrow key */
#define RIGHT        108    /* = L, and 333 was right arrow key */

#define FISH         224
#define HOOK         168
#define FILLCHAR     250

#define CRAB         42
#define BOXFISH      254
#define GUPPIES      247

#define HEADCOL      2
#define BODYCOL      8

           /*  0    1    2    3  */
enum movetype {up,down,left,right};

int snakex [500] = {20,20,20,20,20} , snakey [500] = {19,20,21,22,23};
int fish[  ] = {224,224,224,42,224,224,42,42,157,224,
                224,174,224,239,42,157,42,174,42,224,42,
                157,174,239,247}, maxfish = 24;
int fishleft[25] = {5, 7, 5, 6, 10, 5, 4, 7, 3, 5,
                                     5,3,7,1,12,10,5,7,7,5,
                                     10,7,4,1,1};

int screencolours[5]={LIGHTBLUE, LIGHTCYAN, LIGHTGREEN, LIGHTMAGENTA,
                      LIGHTRED};
int titledata[26] = {3,3,3,3,3,0,0,2,2,2,2,2,1,1,1,1,3,3,3,3,3,
                     2,0,0,0,0};

char sparestr[41];
int snakel = 5, score, hiscore, snakehead = 219, snakebody = 219;
int fishx,fishy, dead, currfish, fishgot, lives = 3, hit, screen;
int fishleftnow, counter, speed, soundmessage, bordercol;

int fishsong[9] = {0,0,1047, 1175,  1480, 1568,  1480, 1568, 1760};
int fishsongl = 8, nodrawx1, nodrawx2, nodrawy1, nodrawy2, nextextra;

int octsong[ ] = {2349, 4, 2349, 4, 2093, 4, 2349, 12,   0, 12, 
                  1975, 4, 1975, 4, 1760, 4, 1975, 12,   0, 12,
                  1975, 2, 1760, 2, 1568, 4, 1568, 4, 1568, 2,
                  2349, 2, 0, 4, 2093, 4, 1975, 4, 1760, 2,
                  1975, 4, 0, 2, 2093, 8, 0, 0,0,0};

int diesound[ ] = {1047, 1480, 2093, 1175, 1661, 2349, 2217, 2093, 0};

enum movetype snakedir = up;

int getrandom (int);
void drawfish (int);
void drawhook (void);
void drawlives (int);
void drawscore (void);
void cleargamearea (int);
void drawsnake(void);
void drawscreenboxes (void);
void drawtitle (void);
void soundplay (void);

void clearkeybuff (void)
{
    while (kbhit())  getkey();
}

void initgame (void)
{

    int a;

    clearkeybuff();
    screen = 0; bordercol = screencolours[(screen / 5) % 5];
    lives = 3; score = 0; speed = 3; nextextra = 500000;
    
    fishleftnow = fishleft[screen % 25] * (screen / 25 + 1);    
    currfish = screen % 25;
}

void resetsnakepos (void)
{
    register int a;

    clearkeybuff();
    counter = 0;
    snakex[0]=6;snakex[1]=5;snakex[2]=4;snakex[3]=3;snakex[4]=2;
    snakey[0]=23;snakey[1]=23;snakey[2]=23;snakey[3]=23;snakey[4]=23;
    snakel = ((screen % 25) + 1) * 5; snakedir = right;
    for (a=5;a 2)
    {
        snakex[5]=20;snakex[6]=20;snakex[7]=20;snakex[8]=20;snakex[9]=20;
        snakey[5]=23;snakey[6]=22;snakey[7]=21;snakey[8]=20;snakey[9]=19;

        for (a=10;a<=delay;a++)
    {
        t = gett();
        while (gett() == t) ;
    }
}

void playoctsong(void) 
{
     int a,b,d;
     a=0;

     do
     {
        sound(octsong[a]/4);
        wait(octsong[a+1]);
        a += 2;
     }
     while (octsong[a] || octsong[a+1]);
    sound(0);
}

void clrtopscr(void)
{
    register int a;
    for (a=1;a<25;a++)
    {
        gotoxy(1,a);
        clreol();
    }
}

int readscreen (int x, int y)
{
    unsigned char *ascii;
    unsigned char value;
    ascii = &value;
    gettext(x,y,x,y,ascii);
    return  (int) value;
}

int isborder (int c)
{
    
    if ( (c > 178 && c < 219) || (c == FILLCHAR) )  return 1;
        else return 0;
}

void newscreen(void)
{
    sound (0);   screen++;
    bordercol = screencolours[(screen / 5) % 5];
    drawsnake();
    
    if (screen > 24)
    {
        speed = 2;        
    }
    cleargamearea(1);    
    
    drawscreenboxes();
    resetsnakepos();
    drawsnake();
    fishleftnow = fishleft[screen % 25] * (screen / 25 + 1);
    
    currfish = screen % 25;
        
    drawscore();
    wait(30);
    clearkeybuff();
}

void hitfish (void)
{

    switch (fish[currfish])
    {
        case 224:
        score += 1000;
        break;

        case 42:
        score += 3000;
        break;

        case 157:
        score += 5000;
        break;

        case 174:
        score += 10000;
        break;

        case 239:
        score += 50000;
        break;

        case 247:
        score += 100000;
        break;
    }

    fishgot++; fishleftnow--;
    drawsnake();
    drawscore();
    soundmessage = 8;

    if (!fishleftnow)
    {
        do
        {
            soundplay();
            wait(speed);
        }
        while (soundmessage > 2);
        sound(784/2); wait(speed);
        sound(880/2); wait(speed);
        sound(988/2); wait(speed);
        sound(1047/2); wait(speed);
        sound(0); soundmessage = 0;
        newscreen();
    }

    if (getrandom (10) < 2) drawhook();

    drawfish(fish[currfish]);
    
    snakex [snakel] = snakex [snakel - 1]; 
    snakey [snakel] = snakey [snakel - 1]; 
    snakel++;
}

void lostlife (void)
{
    int a,b,c;
    c = 0; b = 0;
    if (counter < snakel - 5) c = snakel - 5 - counter;
    sound (0); soundmessage = 0;
    for (a=snakel - 1 - c;a>0;a--)
    
    {
        if (a % 2  && b < 10)
        {
            sound(diesound[b]/4);
            b++;
        }
        wait(1);
        gotoxy(snakex[a],snakey[a]);
        cprintf (" ");
    }

    for (;b<10;b++)
    {
        sound(diesound[b]/4);
        wait(2);
    }
 
    lives -= 1;
    dead = 1;
}

void hitsomething (int hitchar)
{
    int a;

    if ( (isborder (hitchar)) || hitchar == HOOK || hitchar == 219)
        lostlife();

    
    if (hitchar == fish[currfish])
        hitfish();            
}

void drawsnake(void) /* used for initial stationary snake at screen start */
{
    register int a;

    gotoxy(snakex[0],snakey[0]);
    textcolor(HEADCOL);
    cprintf("%c",snakehead);
        
    for (a=1;a= snakel - 5)
    {
        
        lastx = snakex [snakel - 1];
        lasty = snakey [snakel - 1];
        gotoxy (lastx,lasty);   cprintf(" ");
    }
    textcolor(BODYCOL);
    gotoxy (snakex[0],snakey[0]); cprintf("%c",snakebody);


    for (count = snakel - 1 ; count>0 ; count--)
    {
        snakex[count] = snakex[count - 1];
        snakey[count] = snakey[count - 1];
    }
    
    if (movedir == up)
    {
        snakey[0] = snakey[1] - 1;
        nodrawx1 = nodrawx2 = snakex[0];
        nodrawy1 = snakey[0] - 1; nodrawy2 = snakey[0] - 2; 
    }
    
    if (movedir == down)
    {
        snakey[0] = snakey[1] + 1;
        nodrawx1 = nodrawx2 = snakex[0];
        nodrawy1 = snakey[0] + 1; nodrawy2 = snakey[0] + 2;
    }

    if (movedir == left)
    {
        snakex[0] = snakex[1] - 1;
        nodrawy1 = nodrawy2 = snakey[0];
        nodrawx1 = snakex[0] - 1; nodrawx2 = snakex[0] - 2;
     }

    if (movedir == right)
    {
        snakex[0] = snakex[1] + 1;
        nodrawy1 = nodrawy2 = snakey[0];
        nodrawx1 = snakex[0] + 1; nodrawx2 = snakex[0] + 2;
    }
    
    if (!titledraw) 
    {
        ranover = readscreen(snakex[0],snakey[0]);
    
        if (ranover != 32) hitsomething(ranover); 

        if (dead) return;
    }

    textcolor(HEADCOL);
    gotoxy (snakex[0], snakey[0]);  cprintf("%c",snakehead);
}

void keyaction (void)
{
    int key;
    key = getkey();
    
  if (key == UP && (snakedir == left || snakedir == right)) snakedir = up;
  if (key == DOWN && (snakedir == left || snakedir == right)) snakedir = down;
  if (key == LEFT && (snakedir == up || snakedir == down)) snakedir = left;
  if (key == RIGHT && (snakedir == up || snakedir == down)) snakedir = right;
}

void drawborder(int left, int top, int right, int bottom, int colour
         , int filled)
{   
     register int a,b;
     textcolor(colour);

     gotoxy(left,top);   cprintf("%c",218);
     gotoxy(right,top);  cprintf("%c",191);
     gotoxy(left,bottom);  cprintf("%c",192);
     gotoxy(right,bottom);  cprintf("%c",217);

     for(a=left + 1;a nextextra)
    {
        if (lives < 3) lives++;
        drawlives(lives);
        nextextra += 500000;
    }
    
    gotoxy(2,25);
    textcolor(7);
    cprintf("%08d",score);
    gotoxy(22,25);
    cprintf("%02d",screen + 1);
    gotoxy(27,25);
    cprintf("%02d",fishleftnow);
}

void drawhiscore (void)
{
    textcolor (7);
    gotoxy(32,25);
    cprintf("%08d",hiscore);
}    

void drawlives (int lives)
{
    register int a;
    for (a=1;a<21 ; a++)
        cprintf (" ");

}

void cleargamearea(int flag)
{
    register int a,b;

  if (flag == 0)
  {
    for (a=2;a<40;a++)
        for (b=2;b<24;b++)
            {
            gotoxy(a,b);
            if (readscreen(a,b) == 32) continue;
            if (!isborder(readscreen(a,b)))
                cprintf(" ");        /* flag = 0 means don't clear boxes */
            }
  }
  else
  {
    for (a=2;a<24;a++)
        {
        gotoxy(2,a);
            cprintf("                                      ");  
        }
  }

}

int getrandom (int range)
{
    int value;
 /* value = rand() * range / 65535; This caused a segmentation fault with the 
 									newer C compiler! */
    value = rand() % range;
    return value;
}

void drawfish (int thing)
{
    int a,fishx,fishy;
    do
    {
        fishx = getrandom(35) + 2;
        fishy = getrandom(20) + 2;
    }
    while (readscreen (fishx, fishy) != 32);

    gotoxy(fishx, fishy);
    do
    {
        a = getrandom(6) + 9;
    }
    while (a == screencolours[(screen / 5) % 5]);
    textcolor(a);
    cprintf("%c",thing);
}

void drawhook (void)
{
    int fishx,fishy;
    do
    {
        fishx = getrandom(35) + 2;
        fishy = getrandom(20) + 2;
    }
    while (readscreen (fishx, fishy) != 32 || (fishx == nodrawx1 &&
        fishy == nodrawy1) || (fishx == nodrawx2 && fishy == nodrawy2));

    
    gotoxy(fishx, fishy);
    textcolor(7);
    cprintf("%c",HOOK);
}

void titlescreen (void)
{
    int a,c,s;
    struct time ti;
    clearkeybuff();
    clrtopscr();
    gotoxy(13,17);
    textcolor(7);
    cprintf ("ANY KEY FOR GAME");
    gotoxy(14,19);
    textcolor(7);
    cprintf ("ESCAPE TO EXIT");

    gotoxy(3,24);
    textcolor(7);
    cprintf("POINTS    LIVES   POND FISH    HIGH");
    drawscore(); drawhiscore();

    drawtitle();

    gettime(&ti); s = ti.ti_sec / 3 - 3; a = 0; 

    while (!kbhit())
    {
        gettime(&ti);
        if (ti.ti_hund < 10 && s != ti.ti_sec / 3)
        {
            s = ti.ti_sec / 3; c = getrandom (6) + 9;
            gotoxy (12,13); textcolor(7);
            switch (a)
            {
                case 0:
                cprintf("PRESS IJKL TO TURN");
                break;

                case 1:
                cprintf("HOOK ");  textcolor(15);
                cprintf("%c",HOOK); textcolor(7);
                cprintf("  A DEAD EEL");
                break;

                case 2:
                cprintf("   FISH "); textcolor(c);
                cprintf("%c",224); textcolor(7);
                cprintf("  1000   ");
                break;

                case 3:
                cprintf("  MUDCRAB "); textcolor(c);
                cprintf("%c",42); textcolor(7);
                cprintf(" 3000   ");
                break;

                case 4:
                cprintf(" SWORDFISH "); textcolor(c);
                cprintf("%c",157); textcolor(7);
                cprintf(" 5000   ");
                break;
                
                case 5:
                cprintf(" THINFISH "); textcolor(c);
                cprintf("%c",174); textcolor(7);
                cprintf(" 10000  ");
                break;

                case 6:
                cprintf("SHELLFISH "); textcolor(c);
                cprintf("%c",239); textcolor(7);
                cprintf("  50000 ");
                break;

                case 7:
                cprintf(" SCHOOL "); textcolor(c);
                cprintf("%c",247); textcolor(7);
                cprintf("  100000 ");
                break;

                case 8:
                gotoxy (10,13);
                cprintf("EXTRA LIFE EACH 500000");
                break;

                case 9:
                gotoxy(10,13);
                cprintf("  BY SIMON BEER 1997  ");
                break;

            }
            a++; if (a == 10) a = 0;

        }
    }
    hit = getkey();
    clrtopscr();
}

void gameover (void)
{
    clrtopscr();
    gotoxy(16,13);
    textcolor(15);
    cprintf("GAME  OVER");
    wait(30); 
    clrtopscr();
}

void drawscreenboxes (void)
{
    int c;
    c = bordercol;
    drawborder (1,1,40,24,c,0);
    switch (screen % 25)
    {
        case 0:
        case 14:
        break;

        case 1:
        drawborder (5,4,9,7,c,1);
        drawborder (32,4,36,7,c,1);
        drawborder (5,18,9,21,c,1);
        drawborder (32,18,36,21,c,1);
        break;


        case 2:
        case 15:
        drawborder (13,9,29,16,c,1);
        break;

        case 3:
        case 16:
        drawborder (5,4,9,7,c,1);
        drawborder (32,4,36,7,c,1);
        drawborder (5,18,9,21,c,1);
        drawborder (32,18,36,21,c,1);

        drawborder (13,7,17,10,c,1);
        drawborder (24,7,28,10,c,1);
        drawborder (13,15,17,18,c,1);
        drawborder (24,15,28,18,c,1);
        break;

        case 4:
        case 22:
        drawborder (5,4,9,7,c,1);
        drawborder (32,4,36,7,c,1);
        drawborder (5,18,9,21,c,1);
        drawborder (32,18,36,21,c,1);
        drawborder (12,9,29,16,c,1);
        break;

        case 5:
        drawborder (11,5,15,8,c,1);
        drawborder (26,5,30,8,c,1);
        drawborder (11,17,15,20,c,1);
        drawborder (26,17,30,20,c,1);

        drawborder (19,4,22,21,c,1);
        break;

        case 6:
        case 17:
        drawborder (5,4,18,6,c,1);
        drawborder (23,4,36,6,c,1);
        drawborder (5,19,18,21,c,1);
        drawborder (23,19,36,21,c,1);

        drawborder (10,9,13,16,c,1);
        drawborder (28,9,31,16,c,1);
        break;

        case 7:
        case 18:
        drawborder (5,4,9,7,c,1);
        drawborder (32,4,36,7,c,1);
        drawborder (5,18,9,21,c,1);
        drawborder (32,18,36,21,c,1);

        drawborder (13,5,17,8,c,1);
        drawborder (24,5,28,8,c,1);
        drawborder (13,17,17,20,c,1);
        drawborder (24,17,28,20,c,1);
        
        drawborder (4,11,6,14,c,1);
        drawborder (35,11,37,14,c,1);
        break;

        case 8:
        drawborder (6,5,35,20,c,1);
        break;

        case 9:
        case 19:
        drawborder (5,4,9,7,c,1);
        drawborder (32,4,36,7,c,1);
        drawborder (5,18,9,21,c,1);
        drawborder (32,18,36,21,c,1);

        drawborder (13,7,17,10,c,1);
        drawborder (24,7,28,10,c,1);
        drawborder (13,15,17,18,c,1);
        drawborder (24,15,28,18,c,1);
        
        drawborder (4,11,6,14,c,1);
        drawborder (35,11,37,14,c,1);
        drawborder (19,4,22,6,c,1);
        drawborder (19,19,22,21,c,1);
        break;

        case 11:
        case 20:
        drawborder (10,5,14,8,c,1);
        drawborder (27,5,31,8,c,1);
        drawborder (10,17,14,20,c,1);
        drawborder (27,17,31,20,c,1);

        drawborder (19,4,22,21,c,1);

        drawborder (4,11,6,14,c,1);
        drawborder (35,11,37,14,c,1);
        break;

        case 10:
        case 21:
        drawborder (5,4,18,6,c,1);
        drawborder (23,4,36,6,c,1);
        drawborder (5,19,18,21,c,1);
        drawborder (23,19,36,21,c,1);

        drawborder (10,9,13,16,c,1);
        drawborder (28,9,31,16,c,1);

        drawborder (19,11,22,14,c,1);
        break;


        case 12:
        case 23:
        drawborder (5,4,9,7,c,1);
        drawborder (32,4,36,7,c,1);
        drawborder (5,18,9,21,c,1);
        drawborder (32,18,36,21,c,1);

        drawborder (13,5,17,8,c,1);
        drawborder (24,5,28,8,c,1);
        drawborder (13,17,17,20,c,1);
        drawborder (24,17,28,20,c,1);
        
        drawborder (4,11,6,14,c,1);
        drawborder (35,11,37,14,c,1);

        drawborder (19,11,22,14,c,1);
        break;

        default:
        drawborder (5,4,9,7,c,1);
        drawborder (32,4,36,7,c,1);
        drawborder (5,18,9,21,c,1);
        drawborder (32,18,36,21,c,1);

        drawborder (12,7,16,10,c,1);
        drawborder (25,7,29,10,c,1);
        drawborder (12,15,16,18,c,1);
        drawborder (25,15,29,18,c,1);
        
        drawborder (4,11,6,14,c,1);
        drawborder (35,11,37,14,c,1);
        drawborder (19,4,22,6,c,1);
        drawborder (19,19,22,21,c,1);

        drawborder (18,11,23,14,c,1);
        break;
    }
}

void drawletter (int stx, int sty, int length)
{
    register int a;
    int b, dir;

    snakel = 99;  
    for (a=0;a<99;a++)
    {
        snakex[a] = stx;
        snakey[a] = sty;
    }
    
    if (length > 12) b=0;
    else b=21;

    a = b;
    length +=b;

    while (!kbhit() &&  a < length)
    {
        dir = titledata[a];
        movesnake(dir,1);
        wait(3);
        a++;
    }    
}

void drawtitle (void)
{
    
      drawletter (13,7,21);
      drawletter (20,7,21);
      drawletter (28,9,5);
}

void soundplay (void)
{
    if (lives < 1 || dead)
    {
        sound (0);
        return;
    }
    if (soundmessage == 0)
    {

        if (counter % 8 == 1) sound (33);
        if (counter % 8 == 2) sound (0);

        if (counter == 4) sound (36);
        if (counter == 5) sound (0);


        if (counter % 8 == 5  && counter != 5) sound (36);
        if (counter % 8 == 6  && counter != 6) sound (0);
    }
    else
    {
        sound(fishsong[soundmessage] / 2);
        soundmessage--;
    }

}

main()

{
    int a,seed;

    struct time ti;
    for (a=0;a<41;a++)
        sparestr[a] = 250;
    sparestr[41] = '\0';

    gettime (&ti);

    seed = ti.ti_hund * ti.ti_sec;
    srand(seed);

    gppconio_init();
    textmode (1);
    _setcursortype(0);
    
    titlescreen();

  while (hit != 27)
  {
    initgame();
    
    drawscreenboxes ();
    initlife(); drawscore(); drawlives(lives); drawsnake();
    playoctsong();
    do
    {
        if (lives < 3)
        {
            cleargamearea(0);
            initlife();
        
            drawsnake(); drawlives(lives);
            drawscore(); drawhiscore(); 
            wait(20);
        }
        
        wait(4); 
        
        drawfish(fish[currfish]);

        do
        {
            score++;
            drawscore();
            
            if (kbhit()) keyaction();
            movesnake(snakedir,0);
            soundplay();
            if (getrandom (100) < 3) drawhook();
            wait(speed);
        }
        while (!dead);
     sound (0);
     }
     while (lives);

    gameover();
    if (score > hiscore) hiscore = score;
    drawscore(); drawhiscore();
    titlescreen();
  }

    gotoxy (2,2);
    textcolor(7);
    _setcursortype(1); 
    textmode(3);
}

I'll write more later on this website about many of these things, including C programming, and emulators, and virtual machines, and so on...

Cover image by Shutterstock

Byte.Yoga Homepage - Australian Cyber Security Web Magazine

Share This Page

If you liked this page, please share it with others! You can use the links to share on Facebook, Twitter, LinkedIn, Pinterest, and Email. Ther is also an RSS feed to get updates for the website.