Techno-Plaza
Site Navigation [ News | Our Software | Calculators | Programming | Assembly | Downloads | Links | Cool Graphs | Feedback ]
 Main
   Site News

   Our Software

   Legal Information

   Credits

 Calculators
   Information

   C Programming

   Assembly

   Downloads

 Miscellaneous
   Links

   Cool Graphs

   Feedback Form

C Programming Lessons

TIGCC Programming Lessons

The following is part of a series of lessons designed to help teach people to program in C for the TI-89, 92+, and V200 calculators using the TIGCC development environment.

If you wish, you can download the program source code, project files, and binaries here.

Review 3: Intro to Graduate Programming, part 1

Step 1 - Background and Introduction

Welcome to graduate programming, part 1. This lesson will mark your conclusion from beginning programming and allow you to basically code nearly anything you want, so long as you put a little effort into it. After you finish this lesson (provided you have finished or understand all the previous lessons), you will have the necessary skills to do any kind of programming for this platform that you wish. Furthermore, you will be able to use most of these skills to program for other platforms, so, good luck to you first off.

This is a very special lesson, for the reason I mentioned above, and for one more. It marks the completion of my goal with Techno-Plaza. From roughly September 1998, I have worked to build a web site geared towards the TI-89, and later, towards teaching people to program with the TI-89. My idea at first seemed simple: take all the confusing or non-existent crap out there on the various TI sites regarding 68k programming and condense it into a useful, complete resource for people that wanted to learn how to program their calculator. I never imagined that task would take so long.

Although I will try to digress, I want to mark this occasion. I didn't have the benefit of a beginner's programming site, or TIGCC library docs, or even a GUI IDE to program in. None of those things existed in 1998. But, I did have a few things. A TI-89, plusshell (the first kernel for TI-89, which made assembly programming possible), the Fargo docs (from the TI-92 kernel Fargo, which essentially served as a template for programming on the TI-89/92+), and a very well designed, if badly commented source code to a simple game, Nibbles. Before my TI-89, I had a TI-92. Since not many people had a TI-92, there were not a lot of games written for the platform. There were a few, but only a few good ones. Nibbles was one of these. I thought about programming my TI-92. It looked interesting, and might even be fun. So I tried to learn how to do it. I realized there were no docs for such things. Aside from a few tech manuals which assumed a lot about your knowledge, there were few resources to learn. I decided to put off this project. Too much work. Learning an entire language with next to no documentation. Nevermind.

However, after I got my TI-89, TI promised they would have built-in assembly language program support. We wouldn't be forced to hack into the OS to see how it works to get programs we wanted to run. TI lied. Well, maybe that's too harsh, they simply postponed their work for so long by the time it was out, we already developed our own stuff. A few months after the TI-89 was released, and we all realized TI didn't have any plans for rolling out assembly support, someone named Rusty Wagner (author of the venerable albeit now defunt Virtual TI) released a program called PlusShel. PlusShel, based on the Fargo sources since the TI-89 was basically an enhanced TI-92 with a smaller screen. At last, assembly program support was realized. Just one problem. There were no programs to be run. Great. We have a way to run programs, but no programs to run. Ah, (and I'll wrap this up soon I promise), here is where I come in. :-) (I know you all can't wait for this. :-)

My mission: to bring the first TI-89 game to the public! Only one problem, I still only had a basic idea of how to program the TI-92. Luckily, I didn't need much knowledge for what I had to do; be first! That was the only important thing (at the time). So, I grabbed Jimmy Mardell's guide to 68000 programming and started looking at what the Nibbles source code could mean. Due to a very handy fact of the program, I only had to change the level definitions, and the arrow key keycodes to get the program to run on the TI-89. Success! Nibbles 68k was the first assembly program released for the TI-89! I checked ticalc.org and the ti-files (which was once another large archive site before it disappeared). Nobody got anything on before I did, so I'm calling it the first.

Now, here comes Techno-Plaza. Knowing as little as I did about 68k programming, I decided what was needed was to learn more about it. Using the source code to Nibbles (which was later heavily modified to work better, and rewritten a few times) to learn, and Jimmy Mardell's guide (along with some other sources including the PlusShel docs and some docs by Mathieu Lacage), I learned to program 68k fairly decent in about a month.

But that month sucked. It was hard to figure out what all these things meant. The docs I was reading were so poor. They basically assumed that anyone reading them knew as much as they did. And, via trial and error, you eventually did. That's not my idea of a good thing though. My idea was to use the Nibbles source code (fully commented) to teach others how to program in 68k assembly.

Unfortunately, the Nibbles source code was large. There were many parts, and it would take a long time to explain everything so that people (and my target audience is ANYONE who wants to program) would understand the way I do (and NOT doing what the other authors had done, by oversimplifying things, or glazing over things that seem trivial, but are actually important). I quickly realized that a Nibbles programming lesson would be very advanced.

So, not much left to digress from, but nonetheless I do, the founding and developing of Techno-Plaza. And, after nearly 4 years, I am finally ready to teach my first idea, programming using the Nibbles source code. Although, the source bears no resemblance to the original source, the idea is still good in principal.

My 'improved' version of Nibbles is written in C, not 68k assembly, and has many features that did not exist in the original game: difficulty levels, score, um, okay, I guess it's just the two things, but they're important things. So, now that I am finally ending this long intro, let's begin examining the source code to Nibbles 68k v4.2.

Step 2 - The Nibbles Project Source Code

Start TIGCC and begin a new project. We will be creating four source files and 2 header files. This is a large project. Name the source files nibbles, hiscore, gfx, and gameplay. Name the two header files nibbles and gfx. Here is the code for the files.

nibbles.c


/*
 * Nibbles 68k v. 4.2 -- Nibbles Snake Game Clone
 *  Copyright (C) 2000-2002,2007 John David Ratliff
 *  Based on Stilgar's Nibbles for Fargo
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA
 */

#include "nibbles.h"

#include <graph.h>
#include <kbd.h>
#include <stdlib.h>

INT_HANDLER autoint1, autoint5;

// doIntro - display the game intro and wait for menu selections
//  returns FALSE if user selects exit, TRUE otherwise
short int doIntro(short int *gamespeed, short int *gamedifficulty) {
    short int selector = MENUSTART, done = FALSE, key, options = FALSE, selection = 0;
    short int speed = *gamespeed, difficulty = *gamedifficulty;

    // clear the screen
    ClrScr();

    // draw the intro screen
    drawBorder();
    drawLogo();
    drawMenu();
    drawCopyright();
    drawSelector(selector);

    while (!done) {
        // wait for keypress
        while ((key = getKey()) == 0);

        if (key == KUP) {
            // move up the menu
            drawSelector(selector);

            if (selector == MENUSTART) {
                selector = MENUEND;
            } else {
                selector -= MENUSTEP;
            }

            drawSelector(selector);
        } else if (key == KDOWN) {
            // move down the menu
            drawSelector(selector);

            if (selector == MENUEND) {
                selector = MENUSTART;
            } else {
                selector += MENUSTEP;
            }

            drawSelector(selector);
        } else if (key == KLEFT) {
            if (options) {
                // if we are in the options menu
                selection = (selector - MENUSTART) / MENUSTEP;

                // set the speed option
                if (selection == START) {
                    // same as speed option -- option 1 == SPEED
                    drawSpeedOption(speed);

                    if (speed == VERYSLOW) {
                        speed = VERYFAST;
                    } else {
                        speed--;
                    }

                    drawSpeedOption(speed);

                // set the difficulty option
                } else if (selection == OPTIONS) {
                    // same as difficulty option -- option 2 == DIFFICULTY
                    drawDifficultyOption(difficulty);

                    if (difficulty == EASY) {
                        difficulty = HARD;
                    } else {
                        difficulty--;
                    }

                    drawDifficultyOption(difficulty);
                }
            }
        } else if (key == KENTER || key == KRIGHT) {
            // select menu option
            selection = (selector - MENUSTART) / MENUSTEP;

            if (options) {
            // if we're in the options menu

                // exit the options menu
                if (selection == QUIT) {
                    // close options menu
                    options = FALSE;

                    // switch the options and main menus
                    drawOptionsMenu(speed,difficulty);
                    drawMenu();

                    // reset the selector
                    drawSelector(selector);
                    selector = MENUSTART;
                    drawSelector(selector);
                } else if (selection == START) {
                    // same as speed option -- option 1 == SPEED
                    drawSpeedOption(speed);

                    if (speed == VERYFAST) {
                        speed = VERYSLOW;
                    } else {
                        speed++;
                    }

                    drawSpeedOption(speed);
                } else if (selection == OPTIONS) {
                    // same as difficulty option -- option 2 == DIFFICULTY
                    drawDifficultyOption(difficulty);

                    if (difficulty == HARD) {
                        difficulty = EASY;
                    } else {
                        difficulty++;
                    }

                    drawDifficultyOption(difficulty);
                }
            } else {
                // if we chose to start or exit, end the loop
                if (selection == START || selection == QUIT) {
                    done = TRUE;
                } else {
                    // enter the options menu
                    options = TRUE;

                    // switch the main and options menus
                    drawMenu();
                    drawOptionsMenu(speed,difficulty);

                    // reset the selector
                    drawSelector(selector);
                    selector = MENUSTART;
                    drawSelector(selector);
                }
            }
        } else if (key == KESC) {
            // exit the options menu
            if (options) {
                // close options menu
                options = FALSE;

                // switch the options and main menus
                drawOptionsMenu(speed,difficulty);
                drawMenu();

                // reset the selector
                drawSelector(selector);
                selector = MENUSTART;
                drawSelector(selector);
            } else {
                selection = QUIT;
                done = TRUE;
            }
        }

        // wait for keypress to dissipate
        delay(KEYDELAY);
    }

    // set the game options
    *gamespeed = speed;
    *gamedifficulty = difficulty;

    return (selection == START) ? TRUE : FALSE;
}

void _main(void) {
    short int font = FontGetSys(), speed = AVERAGE, difficulty = NORMAL;

    // Save the auto-interrupts
    autoint1 = GetIntVec(AUTO_INT_1);
    autoint5 = GetIntVec(AUTO_INT_5);

    // create the hiscore file, if it doesn't exist
    if (needScoreFile()) {
        createHiScoreTable();
    }

    // Make sure there are no keystrokes left in the buffer
    GKeyFlush();

    // setup medium size font
    FontSetSys(F_6x8);

    // seed the random number generator
    randomize();

    // redirect auto-interrupts 1 and 5
    SetIntVec(AUTO_INT_1,DUMMY_HANDLER);
    SetIntVec(AUTO_INT_5,DUMMY_HANDLER);

    // keep playing until doIntro returns FALSE (exit)
    while (doIntro(&speed,&difficulty)) {
        play(speed,difficulty);
    }

    // restore auto-interrupts
    SetIntVec(AUTO_INT_1,autoint1);
    SetIntVec(AUTO_INT_5,autoint5);

    // restore the standard font
    FontSetSys(font);
}

hiscore.c


/*
 * Nibbles 68k v. 4.2 -- Nibbles Snake Game Clone
 *  Copyright (C) 2000-2002,2007 John David Ratliff
 *  Based on Stilgar's Nibbles for Fargo
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA
 */

#include "nibbles.h"

#include <args.h>
#include <stdio.h>
#include <string.h>

// the high score file data
static const char *scorefile = "nibblehi";

// loadHiScores - loads the hiscores from file into the hiscore table
//  returns TRUE if hiscores were read, FALSE otherwise
short int loadHiScores(SCORE *hiscores) {
    short int success = TRUE;
    FILE *f;

    // open file for reading and check for errors
    if ((f = fopen(scorefile,"rb")) == NULL) {
        success = FALSE;
    } else {
        // read the hiscore table and check for I/O errors
        if (fread(hiscores,sizeof(SCORE),MAX_HISCORES,f) != MAX_HISCORES) {
            success = FALSE;
        }

        fclose(f);
    }

    return success;
}

// saveHiScores - saves the hiscore table to the hiscore file
//  returns TRUE if the scores were saved to file, FALSE otherwise
short int saveHiScores(SCORE *hiscores) {
    const char *filetype = "N68K";
    short int success = TRUE;
    FILE *f;

    // delete any old scorefile before recreating
    unlink(scorefile);

    // open the hiscore file
    if ((f = fopen(scorefile,"wb")) == NULL) {
        success = FALSE;
    } else {
        // write the hiscore table to disk - check for I/O errors
        if (fwrite(hiscores,sizeof(SCORE),MAX_HISCORES,f) != MAX_HISCORES) {
            success = FALSE;
        }

        // write the file tag
        fputc(0,f);
        fputs(filetype,f);
        fputc(0,f);
        fputc(OTH_TAG,f);

        fclose(f);
    }

    return success;
}

// createHiScoreTable - create a blank hiscore table and write it to file
//  return the result of the disk save
short int createHiScoreTable(void) {
    SCORE board[MAX_HISCORES];

    // set all the entries in the table to blank
    memset(board,0,sizeof(SCORE) * MAX_HISCORES);

    return (saveHiScores(board));
}

// needScoreFile - checks whether a hiscore file already exists
//  returns TRUE if there is no hiscore file, FALSE otherwise
short int needScoreFile(void) {
    FILE *f = fopen(scorefile,"rb");
    short int needed = FALSE;

    // if we cannot open the file, we must need to create it
    if (f == NULL) {
        needed = TRUE;
    } else {
        fclose(f);
    }

    return needed;
}

gfx.c


/*
 * Nibbles 68k v. 4.2 -- Nibbles Snake Game Clone
 *  Copyright (C) 2000-2002,2007 John David Ratliff
 *  Based on Stilgar's Nibbles for Fargo
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA
 */

#include "nibbles.h"
#include "gfx.h"

#include <alloc.h>
#include <dialogs.h>
#include <graph.h>
#include <kbd.h>
#include <sprites.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

// centerText - draws a string at the horizontal center of line y
inline void centerText(const char *str, short int y) {
    short int len = (short int)strlen(str), x = ((160 - (len * 6)) / 2);

    DrawStr(x,y,(char *)str,A_XOR);
}

// drawBorder - draws four lines making a box around the screen
inline void drawBorder(void) {
    DrawLine(X1,Y1,X2,Y2,A_NORMAL);
    DrawLine(X3,Y3,X4,Y4,A_NORMAL);
    DrawLine(X5,Y5,X6,Y6,A_NORMAL);
    DrawLine(X7,Y7,X8,Y8,A_NORMAL);
}

// drawLogo - draws the Nibbles 68k logo across the top of the screen
void drawLogo(void) {
    short int loop, offset = 0;

    // draw the logo pieces
    for (loop = 0; loop < (LOGOTILES * LOGOWIDTH); loop+=LOGOWIDTH) {
        Sprite32(loop,0,LOGOHEIGHT,logo+offset,LCD_MEM,SPRT_XOR);
        offset += LOGOHEIGHT;
    }
}

// drawSelector - draws the menu selector bar over the menu options
void drawSelector(short int y) {
    short int loop, offset = 0;

    // draw the selector pieces
    for (loop = 0; loop < (SELECTORTILES * SELECTORWIDTH); loop+=SELECTORWIDTH) {
        Sprite32(loop,y,SELECTORHEIGHT,selector+offset,LCD_MEM,SPRT_XOR);
        offset += SELECTORHEIGHT;
    }
}

// drawCopyright - prints the copyright notice at the bottom of the screen
inline void drawCopyright(void) {
    FontSetSys(F_4x6);
    DrawStr(COPYX,COPYY,(char *)intro[COPYRIGHT],A_XOR);
    DrawStr(URLX,URLY,(char *)intro[URL],A_XOR);
    FontSetSys(F_6x8);
}

// drawMenu - displays the game menu
void drawMenu(void) {
    short int loop;

    // draw the three menu selections
    for (loop = START; loop <= QUIT; loop++) {
        centerText(intro[loop],MENUSTART+(loop*MENUSTEP));
    }
}

// drawSpeedOption - draws the speed setting (i.e. slow, fast, average)
void drawSpeedOption(short int speed) {
    char speedStr[25];

    // truncate to 0-length
    speedStr[0] = 0;

    // create the speed string
    strcat(speedStr,intro[SPEED]);
    strcat(speedStr,speeds[speed]);

    // display the string
    centerText((const char *)speedStr,MENUSTART);
}

// drawDifficultyOption - draws the difficulty setting (i.e. easy, hard, medium)
void drawDifficultyOption(short int difficulty) {
    char difficultyStr[25];

    // truncate to 0-length
    difficultyStr[0] = 0;

    // create the difficulty string
    strcat(difficultyStr,intro[DIFFICULTY]);
    strcat(difficultyStr,difficulties[difficulty]);

    // display the string
    centerText((const char *)difficultyStr,MENUSTART+MENUSTEP);
}

// drawOptionsMenu - draws the options menu items
inline void drawOptionsMenu(short int speed, short int difficulty) {
    drawSpeedOption(speed);
    drawDifficultyOption(difficulty);

    centerText(intro[QUITOPTIONS],MENUSTART+(2*MENUSTEP));
}

// drawBlock - draws a block on the screen (all nibble graphics are composed of blocks)
inline void drawBlock(short int x, short int y, unsigned char *sprite, short int mode) {
    Sprite8(x,y,HEIGHT,sprite,LCD_MEM,mode);
}

// drawToken - draws a life or apple token at (x,y)
inline void drawToken(short int x, short int y, unsigned char *sprite) {
    Sprite8(x,y,TOKEN_HEIGHT,sprite,LCD_MEM,SPRT_XOR);
}

// drawScore - draws the current player score at the lower-left
inline void drawScore(unsigned long int score) {
    printf_xy(SCOREX,SCOREY,"%lu",score);
}

// drawEndMessage - draws the message at games end (i.e. winner, loser, quitter)
void drawEndMessage(short int ending) {
    short int x, offset = 0;
    unsigned short int *sprite = endings[--ending];

    for (x = 8; x < 152; x += 16) {
        Sprite16(x,38,25,sprite+offset,LCD_MEM,SPRT_AND);
        Sprite16(x,38,25,sprite+offset,LCD_MEM,SPRT_OR);
        offset += 25;
    }
}

// drawCrash - draws the crash message across the screen
void drawCrash(void) {
    short int x, offset = 0;

    for (x = 0; x < CRASHWIDTH; x += 32) {
        Sprite32(CRASHX+x,CRASHY,CRASHHEIGHT,crash+offset,LCD_MEM,SPRT_AND);
        Sprite32(CRASHX+x,CRASHY,CRASHHEIGHT,crash+offset,LCD_MEM,SPRT_OR);
        offset+=CRASHHEIGHT;
    }

    // wait for keypress
    delay(KEYDELAY);
    while (!getKey());
    delay(KEYDELAY);
}

// drawLives - displays the life indicator using life tokens
void drawLives(short int lives) {
    short int loop, x = 76, max = lives;

    if (max > MAX_LIVES) {
        max = MAX_LIVES;
    }

    // draw a life token for each remaining life
    for (loop = 0; loop < max; loop++) {
        x+=4;
        drawToken(x,97,lifeToken);
        x++;
    }
}

// drawApples - displays remaining apples using apple tokens
void drawApples(short int apples) {
    short int loop, x = 76;

    // draw one apple token for each remaining apple
    for (loop = 0; loop < apples; loop++) {
        x+=4;
        drawToken(x,94,appleToken);
        x++;
    }
}

// drawApple - draws an apple on screen at a randomly selected location
//  returns - the position of the newly drawn apple
POSITION drawApple(short int level, SNAKE *snake) {
    POSITION pos = {0,0};

    // find a suitable random position for the apple
    while (hasHitWall(pos,level) || hasHitSnake(pos,snake,SNAKE_HIT_APPLE)) {
        pos.x = random(XTILES);
        pos.y = random(YTILES);
    }

    // draw the apple sprite
    drawBlock(pos.x * WIDTH, pos.y * HEIGHT, apple, SPRT_XOR);

    // return the apple's position
    return pos;
}

// eraseApple - erases an apple token indicating an apple has just been eaten
inline void eraseApple(short int apples) {
    drawToken(76+4+apples*4+apples,94,appleToken);
}

// drawSnake - draws the snake on the LCD
void drawSnake(SNAKE *snake) {
    POSITION pos;
    short int loop;

    // draw the snake segments
    for (loop = 0; loop < snake->length; loop++) {
        pos = snake->location[loop];
        drawBlock(pos.x * WIDTH, pos.y * HEIGHT, block, SPRT_OR);
    }

    // remove the last segment to create the illusion of movement
    pos = snake->location[snake->length];

    if (pos.x != 0 && pos.y != 0) {
        drawBlock(pos.x * WIDTH, pos.y * HEIGHT, empty, SPRT_AND);
    }
}

// clearDisplayLine - removes the bottom 3 lines of the bottom border
//          for extra indicator display room
void clearDisplayLine(void) {
    short int loop;

    for (loop = 92; loop < 95; loop++) {
        DrawLine(0,loop,160,loop,A_REVERSE);
    }
}

// drawLevel - draws one of the maze levels
void drawLevel(short int level) {
    short int yLoop,xLoop;
    long int area, mask;

    // clear the screen first
    ClrScr();

    for (yLoop = 0; yLoop < YTILES; yLoop++) {
        area = levels[level][yLoop];
        mask = 0x100000;

        // start the x loop
        for (xLoop = 0; xLoop < XTILES; xLoop++) {
            // shift the mask index
            mask >>= 1;

            // if the level data says to put a block here, then do it
            if (area & mask) {
                drawBlock(xLoop * WIDTH, yLoop * HEIGHT, block, SPRT_OR);
            }
        }
    }

    // share the bottom line of the maze
    clearDisplayLine();
}

// drawHiScoreBoard - draws the hiscore board, and updates it with the hiscore from the latest game
void drawHiScoreBoard(unsigned long int newscore) {
    SCORE scores[MAX_HISCORES];
    short int loop, pos = -1;
    char name[10], str[50];
    const char *error = "File I/O Error";
    HANDLE dlg;

    // restore auto-interrupts
    SetIntVec(AUTO_INT_1,autoint1);
    SetIntVec(AUTO_INT_5,autoint5);

    if (!loadHiScores(scores)) {
        // cannot open hiscore file -- display error
        DlgMessage(error,"Unable to Load HiScore Data",BT_OK,BT_NONE);
        return;
    }

    // check if latest score is a highscore
    for (loop = (MAX_HISCORES - 1); loop >= 0; loop--) {
        if (newscore > scores[loop].score) {
            // new HiScore!!
            pos = loop;
        }
    }

    if (pos != -1) {
        // if we found a new hiscore
        if ((dlg = DialogNewSimple(DLGWIDTH,DLGHEIGHT)) == H_NULL) {
            DlgMessage("Memory Allocation Error","Not Enough Free Memory!",BT_OK,BT_NONE);
        } else {
            DialogAddTitle(dlg,"New Hiscore!",BT_OK,BT_NONE);
            DialogAddRequest(dlg,5,25,"Your Name:",0,9,11);

            sprintf(str,"You earned the #%hd hiscore position!",pos+1);
            DialogAddText(dlg,5,15,str);

            do {
                // truncate name variable
                name[0] = 0;
            } while (DialogDo(dlg,CENTER,CENTER,name,NULL) != KEY_ENTER);

            // free the dialog box memory
            HeapFree(dlg);

            // move the hiscore list down
            if (pos < (MAX_HISCORES - 1)) {
                for (loop = (MAX_HISCORES - 1); loop > pos; loop--) {
                    scores[loop].score = scores[loop - 1].score;
                    scores[loop].name[0] = 0;
                    strcpy(scores[loop].name,scores[loop - 1].name);
                }
            }

            // fill in the new hiscore
            scores[pos].score = newscore;
            scores[pos].name[0] = 0;
            strcpy(scores[pos].name,name);

            if (!saveHiScores(scores)) {
                DlgMessage(error,"Unable to save HiScore Board",BT_OK,BT_NONE);
            }
        }
    }

    // display the hiscore board

    // clear the screen
    ClrScr();

    // draw the screen borders
    drawBorder();

    // draw the game logo
    drawLogo();

    FontSetSys(F_8x10);
    DrawStr(25,35,"Hiscore Board",A_NORMAL);
    FontSetSys(F_6x8);

    for (loop = 0; loop < 5; loop++) {
        printf_xy(20,50+loop*10,"#%hd %-9s %lu",loop+1,scores[loop].name,scores[loop].score);
    }

    ngetchx();

    // redirect auto-interrupts 1 and 5
    SetIntVec(AUTO_INT_1,DUMMY_HANDLER);
    SetIntVec(AUTO_INT_5,DUMMY_HANDLER);

    // wait for keypresses to dissipate
    delay(KEYDELAY);
}

gameplay.c


/*
 * Nibbles 68k v. 4.2 -- Nibbles Snake Game Clone
 *  Copyright (C) 2000-2002,2007 John David Ratliff
 *  Based on Stilgar's Nibbles for Fargo
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA
 */

#include "nibbles.h"

#include <kbd.h>
#include <stdlib.h>

// delay - slows the program down
void delay(short int value) {
    value <<= 3;

    while (value-- > 0) {
        rand();
    }
}

// getKey - checks for keypresses
//  returns - non-standard keycode of the key pressed, or 0 if no key was pressed
inline short int getKey(void) {
    if (_rowread(ARROWROW) & UPKEY) {
        return KUP;
    } else if (_rowread(ARROWROW) & DOWNKEY) {
        return KDOWN;
    } else if (_rowread(ARROWROW) & LEFTKEY) {
        return KLEFT;
    } else if (_rowread(ARROWROW) & RIGHTKEY) {
        return KRIGHT;
    } else if (_rowread(ESCROW) & ESCKEY) {
        return KESC;
    } else if (_rowread(ENTERROW) & ENTERKEY) {
        return KENTER;
    } else if (_rowread(CHEATROW) & CHEATKEY) {
        return KCHEAT;
    }

    return 0;
}

// hasHitApple - checks to see whether the snake ate an apple
//  returns - TRUE if they hit the apple, FALSE otherwise
short int hasHitApple(POSITION apple, POSITION *snake) {
    short int snagged = FALSE;

    // if the position and the apple position match, we snagged it
    if ((apple.x == snake[0].x) && (apple.y == snake[0].y)) {
        snagged = TRUE;
    }

    // otherwise, we didn't
    return snagged;
}

// hasHitWall - checks to see whether the snake hit a wall
//  returns - TRUE if snake hit wall, FALSE otherwise
short int hasHitWall(POSITION pos, short int level) {
    long int mask = 0x80000, area;
    short int crash = FALSE;

    // find the search area
    area = levels[level][pos.y];
    mask = mask >> pos.x;

    // if we have a match, we crashed
    if (area & mask) {
        crash = TRUE;
    }

    return crash;
}

// hasHitSnake - checks to see if the snake hit itself
//  returns - TRUE if the snake hit itself, FALSE otherwise
short int hasHitSnake(POSITION pos, SNAKE *snake, short int start) {
    short int loop;

    // loop through the body of the snake
    for (loop = start; loop < snake->length; loop++) {
        // if the position and the snake match, we hit the snake
        if ((snake->location[loop].x == pos.x) && (snake->location[loop].y == pos.y)) {
            return TRUE;
        }
    }

    return FALSE;
}

// getApplePoints - calculates the number of points the apple is worth
//  returns - points apple is worth
short int getApplePoints(short int level, short int speed, short int difficulty) {
    short int speedBonus;

    // determine speed bonus
    if (speed <= MEDIUM) {
        speedBonus = 0;
    } else if (speed > MEDIUM && speed < QUICK) {
        speedBonus = 25;
    } else if (speed > AVERAGEPLUS && speed < VERYFAST) {
        speedBonus = 50;
    } else { // speed = VERYFAST
        speedBonus = 100;
    }

    // calculate and return bonus
    return (((level * 5) + 10 + (speedBonus)) * (++difficulty));
}

// initSnake - sets up the snake parameters
void initSnake(SNAKE *snake) {
    short int loop;

    // reset the snake to standard
    snake->dead = FALSE;
    snake->length = 3;
    snake->direction = RIGHT;

    // setup the initial position
    for (loop = 0; loop < 4; loop++) {
        snake->location[loop].x = 5 - loop;
        snake->location[loop].y = 3;
    }

    // clear all the excess positions
    for (loop = 3; loop < 28; loop++) {
        snake->location[loop].x = 0;
        snake->location[loop].y = 0;
    }
}

// growSnake - makes the snake grow
void growSnake(SNAKE *snake) {
    short int loop;

    // add snake positions for growth
    for (loop = snake->length; loop < (snake->length+SNAKE_GROW); loop++) {
        snake->location[loop].x = snake->location[snake->length].x;
        snake->location[loop].y = snake->location[snake->length].y;
    }

    // adjust the snakes length
    snake->length += SNAKE_GROW;
}

// moveSnake - moves the snake across the screen
void moveSnake(SNAKE *snake) {
    short int loop;

    // relocate the positions
    for (loop = snake->length; loop >= 0; loop--) {
        snake->location[loop] = snake->location[loop-1];
    }

    // grab the second position
    snake->location[0].x = snake->location[1].x;
    snake->location[0].y = snake->location[1].y;

    // adjust the snake based on the direction
    if (snake->direction == UP) {
        snake->location[0].y--;
    } else if (snake->direction == DOWN) {
        snake->location[0].y++;
    } else if (snake->direction == LEFT) {
        snake->location[0].x--;
    } else if (snake->direction == RIGHT) {
        snake->location[0].x++;
    }

    // re-draw the snake at the new position
    drawSnake(snake);
}

// play - the main game loop
void play(short int speed, short int difficulty) {
    SNAKE snake, *snakeptr = &snake;
    short int totalApples, levels, speedDelay, done = FALSE, applePoints, level = 0, apples;
    short int key = 0, cheater = FALSE, begin, loop, keyDelay, ending = NONE;
    unsigned long int score = 0;
    POSITION currentApple;

    // difficulty level values
    const short int startLives[3] = {6, 4, 2};
    const short int startApples[3] = {5, 8, 12};
    const short int startLevels[3] = {5, 8, 10};

    // speed delay
    const short int startDelay[10] = {750,650,600,550,425,375,325,250,175,100};

    // setup the game options
    snake.lives = startLives[difficulty];
    totalApples = startApples[difficulty];
    levels = startLevels[difficulty];
    speedDelay = startDelay[speed];

    // the main game loop
    while (!done) {
        // setup game play
        drawLevel(level);
        initSnake(snakeptr);
        drawSnake(snakeptr);
        drawScore(score);
        currentApple = drawApple(level, snakeptr);
        applePoints = getApplePoints(level, speed, difficulty);
        apples = totalApples;
        drawApples(apples);
        drawLives(snake.lives);

        begin = FALSE;
        while (!begin) {
            while ((key = getKey()) == 0);

            if (key == KESC) {
                snake.dead = TRUE;
                begin = TRUE;
                done = TRUE;
                ending = QUITTER;
            } else if (key == KENTER) {
                begin = TRUE;
            }

            // wait for keypresses to dissipate
            delay(KEYDELAY);
        }

        while (!snake.dead && apples > 0) {
            for (loop = 0; loop < speedDelay; loop++) {
                if ((key = getKey()) != 0) {
                    if (speed < MEDIUM) {
                        keyDelay = (6 * KEYDELAY) / 7;
                    } else if (speed < AVERAGEPLUS) {
                        keyDelay = (2 * KEYDELAY) / 3;
                    } else if (speed < FAST) {
                        keyDelay = (53 * KEYDELAY) / 100;
                    } else {
                        keyDelay = KEYDELAY / 2;
                    }

                    delay(keyDelay);

                    break;
                }
            }

            switch (key) {
                case KLEFT:
                    if (snake.direction != RIGHT) {
                        snake.direction = LEFT;
                    }
                    break;
                case KUP:
                    if (snake.direction != DOWN) {
                        snake.direction = UP;
                    }
                    break;
                case KDOWN:
                    if (snake.direction != UP) {
                        snake.direction = DOWN;
                    }
                    break;
                case KRIGHT:
                    if (snake.direction != LEFT) {
                        snake.direction = RIGHT;
                    }
                    break;
                case KCHEAT:
                    if (cheater == TRUE) {
                        if (level < levels) {
                            level++;
                        }

                        apples = -1;
                    }

                    cheater = TRUE;
                    break;
                case KESC:
                    done = TRUE;
                    snake.dead = TRUE;
                    ending = QUITTER;
                    break;
                case KENTER:
                    // pause game
                    delay(KEYDELAY);
                    while (getKey() != KENTER);
                    delay(KEYDELAY);
                    break;
            }

            // move the snake
            moveSnake(snakeptr);

            // check for snake hitting things
            if (hasHitWall(snake.location[0], level)) {
                // hit wall
                drawCrash();
                snake.dead = TRUE;
            } else if (hasHitSnake(snake.location[0], snakeptr, SNAKE_HIT_SELF)) {
                // hit self
                drawCrash();
                snake.dead = TRUE;
            } else if (hasHitApple(currentApple,snake.location)) {
                // ate apple
                growSnake(snakeptr);
                currentApple = drawApple(level,snakeptr);
                score += applePoints;
                drawScore(score);
                apples--;
                eraseApple(apples);

                if (apples == 0) {
                    // next level, add point and life bonuses
                    level++;
                    score+=500*difficulty+10*snake.lives;
                    snake.lives++;

                    if (level > levels) {
                        ending = WINNER;
                        done = TRUE;
                        score += difficulty*10000;
                    }
                }
            }
        }

        if (snake.dead && !cheater) {
            // cheaters get infinite lives, but they pay for it
            if (--(snake.lives) < 0) {
                done = TRUE;
                ending = LOSER;
            }
        }
    }

    if (cheater) {
        score = 0;
        ending = CHEATER;
    }

    // display ending message
    drawEndMessage(ending);

    // wait for keypress before continuing
    delay(KEYDELAY);
    while (!getKey());
    delay(KEYDELAY);

    // display the hiscore board
    drawHiScoreBoard(score);
}

nibbles.h


/*
 * Nibbles 68k v. 4.2 -- Nibbles Snake Game Clone
 *  Copyright (C) 2000-2002,2007 John David Ratliff
 *  Based on Stilgar's Nibbles for Fargo
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA
 */
#ifndef _NIBBLES_H
#define _NIBBLES_H

// Compile for specific Calc
// Comment out the next line to compile for TI-92+/V200
#define USE_TI89

#ifndef USE_TI89
    #define USE_TI92PLUS
    #define USE_V200
#endif

#include <intr.h>

/* Structures */

typedef struct {
    char name[10];           // player's name
    unsigned long int score; // their score
} SCORE;

typedef struct {
    unsigned short int x:8, y:8;
} POSITION;

typedef struct {
    POSITION location[28];
    signed short int lives;
    unsigned short int dead:1, direction:2, length:13;
} SNAKE;

/* Constants */

#define MAX_HISCORES 5
#define MAX_LIVES    16

#ifdef USE_TI89
    #define DLGWIDTH  140
    #define DLGHEIGHT 45

    enum KeyMatrix {
        ESCROW = ~0x0040, ESCKEY = 0x0001, CHEATROW = ~0x0020, CHEATKEY = 0x0010,
        ENTERROW = ~0x0002, ENTERKEY = 0x0001, ARROWROW = ~0x0001,
        UPKEY = 0x0001, LEFTKEY = 0x0002, DOWNKEY = 0x0004, RIGHTKEY = 0x0008,
    };
#else
    #define DLGWIDTH 225
    #define DLGHEIGHT 55

    enum KeyMatrix {
        ESCROW = ~0x0100, ESCKEY = 0x0040, CHEATROW = ~0x0080, CHEATKEY = 0x0001,
        ENTERROW = ~0x0200, ENTERKEY = 0x0002, ARROWROW = ~0x0001, UPKEY = 0x0020,
        LEFTKEY = 0x0010, DOWNKEY = 0x0080, RIGHTKEY = 0x0040
    };
#endif

enum Intro      {START,OPTIONS,QUIT,COPYRIGHT,URL,SPEED,DIFFICULTY,QUITOPTIONS};
enum Speeds     {VERYSLOW,SLOW,MEDSLOW,LEISURELY,MEDIUM,AVERAGE,AVERAGEPLUS,QUICK,FAST,VERYFAST};
enum Difficulty {EASY,NORMAL,HARD};
enum Logo       {LOGOWIDTH = 32, LOGOHEIGHT = 20, LOGOTILES = 5};
enum Selector   {SELECTORWIDTH = 32, SELECTORHEIGHT = 8, SELECTORTILES = 5};
enum Menu       {MENUSTART = 35, MENUEND = 55, MENUSTEP = 10};
enum Copyright  {COPYX = 15, COPYY = 85, URLX = 25, URLY = 92};
enum WaitKey    {NOWAIT, WAIT};
enum Delay      {KEYDELAY = 415};
enum Keys       {KUP = 1,KDOWN,KLEFT,KRIGHT,KESC,KENTER,KCHEAT};
enum Directions {UP,LEFT,DOWN,RIGHT};
enum Dimensions {WIDTH = 8, HEIGHT = 5, TOKEN_HEIGHT = 2, XTILES = 20, YTILES = 19};
enum Score      {SCOREX = 10, SCOREY = 93};
enum Snake      {SNAKE_HIT_APPLE, SNAKE_HIT_SELF, SNAKE_GROW};
enum Crash      {CRASHX = 32, CRASHY = 40, CRASHHEIGHT = 20, CRASHWIDTH = 88};
enum Endings    {NONE,QUITTER,LOSER,CHEATER,WINNER,ENDY = 45};
enum Borders    {X1 = 0, X2 = 0, X3 = 0, X4 = 159, X5 = 0, X6 = 159, X7 = 159, X8 = 159,
                 Y1 = 0, Y2 = 99, Y3 = 0, Y4 = 0, Y5 = 99, Y6 = 99, Y7 = 0, Y8 = 99};

/* External Variable Declarations */

extern INT_HANDLER autoint1, autoint5;
extern unsigned long int levels[10][19];

/* Function proto-types */

/* nibbles.c */

short int doIntro(short int *, short int *);
void _main(void);

/* hiscore.c */

short int loadHiScores(SCORE *);
short int saveHiScores(SCORE *);
short int createHiScoreTable(void);
short int needScoreFile(void);

/* gfx.c */

inline void centerText(const char *, short int);
inline void drawBorder(void);
void drawLogo(void);
void drawSelector(short int);
inline void drawCopyright(void);
void drawMenu(void);
void drawSpeedOption(short int);
void drawDifficultyOption(short int);
inline void drawOptionsMenu(short int, short int);
inline void drawBlock(short int, short int, unsigned char *, short int);
inline void drawToken(short int, short int, unsigned char *);
inline void drawScore(unsigned long int);
void drawEndMessage(short int);
void drawCrash(void);
void drawLives(short int);
void drawApples(short int);
POSITION drawApple(short int, SNAKE *);
inline void eraseApple(short int);
void drawSnake(SNAKE *);
void clearDisplayLine(void);
void drawLevel(short int);
void drawHiScoreBoard(unsigned long int);

/* gameplay.c */

void delay(short int);
inline short int getKey(void);
short int hasHitApple(POSITION, POSITION *);
short int hasHitWall(POSITION, short int);
short int hasHitSnake(POSITION, SNAKE *, short int);
void initSnake(SNAKE *);
void growSnake(SNAKE *);
void moveSnake(SNAKE *);
void play(short int, short int);

#endif

gfx.h


/*
 * Nibbles 68k v. 4.2 -- Nibbles Snake Game Clone
 *  Copyright (C) 2000-2002,2007 John David Ratliff
 *  Based on Stilgar's Nibbles for Fargo
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA
 */

#ifndef _GFX_H
#define _GFX_H

// game sprites
static unsigned char block[] = {0xFF,0xFF,0xFF,0xFF,0xFF,0x00};
static unsigned char empty[] = {0x00,0x00,0x00,0x00,0x00,0x00};
static unsigned char apple[] = {0x18,0x3C,0x7E,0x7E,0x3C,0x00};

// game info sprites
static unsigned char lifeToken[] = {0xF0, 0xF0};
static unsigned char appleToken[] = {0xA0, 0x50};

// level data
unsigned long int levels[10][19] = {
    {0xFFFFF,0x80001,0x80001,0x80001,0x80001,0x80001,0x80001,0x80001,
     0x80001,0x80001,0x80001,0x80001,0x80001,0x80001,0x80001,0x80001,
     0x80001,0x80001,0xFFFFF}, // level 1
    {0xFFFFF,0x80001,0x80001,0x80001,0x80001,0x80001,0x80001,0x80001,
     0x80001,0x87FE1,0x80001,0x80001,0x80001,0x80001,0x80001,0x80001,
     0x80001,0x80001,0xFFFFF}, // level 2
    {0xFFFFF,0x80001,0x80001,0x80001,0x80001,0x87FE1,0x80001,0x80001,
     0x80001,0x87FE1,0x80001,0x80001,0x80001,0x87FE1,0x80001,0x80001,
     0x80001,0x80001,0xFFFFF}, // level 3
    {0xFFFFF,0x80001,0x80001,0x80001,0x80001,0x84001,0x82001,0x81001,
     0x80801,0x80401,0x80201,0x80101,0x80081,0x80041,0x80021,0x80001,
     0x80001,0x80001,0xFFFFF}, // level 4
    {0xFFFFF,0x80001,0x80001,0x80001,0x80401,0x80401,0x80401,0x80401,
     0x80401,0x87FC1,0x80401,0x80401,0x80401,0x80401,0x80401,0x80001,
     0x80001,0x80001,0xFFFFF}, // level 5
    {0xFFFFF,0x80101,0x80101,0x80101,0x80101,0x80001,0x80001,0x80001,
     0xF8001,0x80001,0x80001,0x8001F,0x80001,0x80001,0x80801,0x80801,
     0x80801,0x80801,0xFFFFF}, // level 6
    {0xFFFFF,0x80001,0x80001,0x80001,0x81F81,0x80001,0x80001,0x90009,
     0x90009,0x90009,0x90009,0x90009,0x80001,0x80001,0x81F81,0x80001,
     0x80001,0x80001,0xFFFFF}, // level 7
    {0xFFFFF,0x80001,0x80001,0x80001,0x80001,0xFFF01,0x80001,0x80001,
     0x80001,0x80FFF,0x80001,0x80001,0x80001,0xFFF01,0x80001,0x80001,
     0x80001,0x80001,0xFFFFF}, // level 8
    {0xFFFFF,0x80001,0x80F81,0x80001,0x84011,0x84011,0x84211,0x84211,
     0x80201,0x80001,0x84011,0x84011,0x84011,0x84211,0x80201,0x84211,
     0x84011,0x80001,0xFFFFF}, // level 9
    {0xFFFFF,0x80001,0x80001,0x80001,0x9FC11,0x90411,0x90411,0x90411,
     0x90411,0x90631,0x80001,0x90631,0x90411,0x90411,0x9FC11,0x80001,
     0x80001,0x80001,0xFFFFF}, // level 10 (last level)
};

// game logo
static unsigned long int logo[100] = {
    0x00000000,0x00000000,0x00000000,0x000e000f,0x01ff3f9f,
    0x007f3f1f,0x007f9f0f,0x007f9f3f,0x007fdf1f,0x007fdf0f,
    0x007ffe0f,0x00fdfe0f,0x00fcfe1f,0x00fefe1f,0x00fe7e1f,
    0x00fe3e1f,0x01ff1f1f,0x001f883f,0x00000000,0x00000000,
    0x00000000,0x00000000,0x00000000,0x87c03e00,0xcf807c01,
    0xc7803c03,0x87803c01,0xe7b83dc1,0x877e3bf1,0x87fe3ff1,
    0x87ff3ff9,0x87bf3df9,0x87bf3df9,0x87ff3ff9,0x87fe3ff1,
    0x87fc3fe1,0xc7f83fc1,0xef807c03,0x00000000,0x00000000,
    0x00000000,0x00000000,0x00000000,0x18000000,0xf8000000,
    0xf8000000,0xf8000000,0xf83f03f2,0xf87f87fc,0xf8efc7fc,
    0xf9cfef88,0xf1ffc7fc,0xf1ffc3fe,0xf1ffa07f,0xf1fe667f,
    0xf0ffefff,0xf07feffe,0xf81e28f8,0x00000000,0x00000000,
    0x00000000,0x00000000,0x00000000,0x0000f080,0x0003ff01,
    0x0007fe03,0x000f8603,0x001f0001,0x001f7e01,0x003fff87,
    0x003fffcf,0x003f9fcf,0x003f9fdf,0x001fffdf,0x001fff8f,
    0x000fff0f,0x0007fe07,0x0000f000,0x00000000,0x00000000,
    0x00000000,0x00000000,0x00000000,0xfc0ff000,0xff07e000,
    0x9f07c000,0xff07c800,0xff07df80,0xff07de00,0xffc7fc00,
    0xffe7f800,0xfce7fc00,0xfce7fe00,0xffe7ff00,0xffe7df80,
    0xffc7cf80,0xff87e780,0xfc0fee00,0x00000000,0x00000000
};

// menu selector block
static unsigned long int selector[40] = {
    0x03ffffff,0x03ffffff,0x03ffffff,0x03ffffff,0x03ffffff,0x03ffffff,0x03ffffff,0x03ffffff,
    0xffffffff,0xffffffff,0xffffffff,0xffffffff,0xffffffff,0xffffffff,0xffffffff,0xffffffff,
    0xffffffff,0xffffffff,0xffffffff,0xffffffff,0xffffffff,0xffffffff,0xffffffff,0xffffffff,
    0xffffffff,0xffffffff,0xffffffff,0xffffffff,0xffffffff,0xffffffff,0xffffffff,0xffffffff,
    0xffffffc0,0xffffffc0,0xffffffc0,0xffffffc0,0xffffffc0,0xffffffc0,0xffffffc0,0xffffffc0
};

// crash logo
static unsigned long int crash[60] = {
    0x00000000,0x00000000,0x01ff6000,0x01ff6000,0x07807800,
    0x07807800,0x00000000,0x078000f1,0x078000f1,0x078000fe,
    0x078000fe,0x00000000,0x078000f0,0x078000f0,0x078078f0,
    0x078078f0,0x01ff60f0,0x01ff60f0,0x00000000,0x00000000,
    0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,
    0x00000000,0x00000000,0xe0ffb03f,0xe0ffb03f,0x00003cf0,
    0x00003cf0,0x00000000,0x00ffbc3f,0x00c03c00,0x03c03c00,
    0x03c03c00,0x00ffbc3f,0x00ffbc3f,0x00000000,0x00000000,
    0x00000000,0x00000000,0x001e0000,0x001e0000,0x001e0000,
    0x001e0000,0x00000000,0xec1ffd80,0xec1ffd80,0x001e01e0,
    0x001e01e0,0x00000000,0xec1e01e0,0x0c1e01e0,0x0f1e01e0,
    0x0f1e01e0,0xec1e01e0,0xec1e01e0,0x00000000,0x00000000
};

static unsigned short int endings[4][225] = {
    {0x0000,0x0000,0x069c,0x0fff,0x1fff,
     0x3fff,0x3fbf,0x7f97,0x7e03,0x7e03,
     0x7c01,0xf801,0xf801,0xfc05,0xfe05,
     0xfe3f,0x7f1f,0x7f9f,0x3fff,0x1fff,
     0x1fff,0x0dff,0x0067,0x0002,0x0000,
     0x0000,0x0007,0x002f,0x803e,0xc0be,
     0xe0fc,0xe07c,0xf07e,0xf87e,0xf8fe,
     0xf8fe,0xf8fe,0xf07e,0xf87e,0xf87f,
     0xf0ff,0xf07f,0xe07f,0xc03f,0x803f,
     0xc01f,0xf017,0x8001,0x0000,0x0000,
     0x0000,0x0000,0x02d1,0x0fd1,0x0ff1,
     0x07f1,0x0ff1,0x0fe1,0x07e3,0x07d3,
     0x0ff3,0x07f3,0x07e1,0x07e1,0x07f2,
     0x03f3,0x07e1,0x03e1,0x87e1,0xffe3,
     0xff43,0xfec3,0x6c00,0x0000,0x0000,
     0x0000,0x1c09,0xfc3f,0xfc3f,0xf87f,
     0xf879,0xfc01,0xf801,0xf800,0xf800,
     0xfc00,0xfc00,0xfc00,0xfc00,0xf800,
     0xf000,0xf800,0xf800,0xf000,0xf800,
     0xfc00,0xfc00,0x2000,0x0000,0x0000,
     0x0000,0xc788,0xfff8,0xfff0,0xfff8,
     0xfff8,0xfe18,0x7e00,0x7f00,0x7f00,
     0x7f00,0x7e00,0xfe00,0x7e00,0x7e00,
     0xfe00,0xfe00,0x7e00,0x7e00,0xfc00,
     0xfe00,0xfe00,0xb600,0x0000,0x0000,
     0x0000,0x138f,0x7fff,0x7fff,0xffff,
     0xf3ff,0x03fc,0x02fc,0x00fe,0x00fe,
     0x00fe,0x00fc,0x01fc,0x00fc,0x00fc,
     0x01fc,0x01fc,0x00fc,0x00fc,0x01f8,
     0x01fc,0x01fc,0x016c,0x0000,0x0000,
     0x0000,0x1000,0xf0bf,0xe07f,0xf07f,
     0xf07f,0x307e,0x00ff,0x00fe,0x00fc,
     0x00fe,0x007f,0x007f,0x01ff,0x00fe,
     0x00fe,0x00fe,0x00fc,0x007f,0x00ff,
     0x00ff,0x00ff,0x000f,0x0000,0x0000,
     0x0000,0x0000,0xff08,0xfe0f,0xfe0f,
     0xfe0f,0x000f,0x000f,0x000f,0x0007,
     0x0e1f,0xfe0f,0xfe0f,0xfe0f,0x180f,
     0x000f,0x001f,0x001f,0x3a0f,0xfe0f,
     0xff0f,0xff1b,0x0e01,0x0000,0x0000,
     0x0000,0x0600,0xfee0,0xffe0,0xfffc,
     0xfffc,0xedfe,0xc4fe,0xc0fc,0xf2fc,
     0xe7f8,0xfffc,0xfff8,0xfff0,0xfff0,
     0xfdf8,0xc5f8,0xc1fc,0xc1f8,0xc1fc,
     0xc0fc,0xe0fe,0x60de,0x0000,0x0000},   // quitter
    {0x0000,0x0000,0x0000,0x0000,0x0000,
     0x0000,0x0000,0x0000,0x0000,0x0000,
     0x0000,0x0000,0x0000,0x0000,0x0000,
     0x0000,0x0000,0x0000,0x0000,0x0000,
     0x0000,0x0000,0x0000,0x0000,0x0000,
     0x0000,0x0000,0x1020,0x3fc0,0x7fc0,
     0x7f80,0x3fc0,0x3fc0,0x3fc0,0x3fc0,
     0x7fc0,0x3f80,0x3f40,0x3fc0,0x3f80,
     0x3fc0,0x1f80,0x7f80,0x3fde,0x3fff,
     0x3fff,0x3fff,0x3fff,0x701c,0x0000,
     0x0000,0x0000,0x0000,0x000f,0x001f,
     0x003f,0x003f,0x017f,0x01fc,0x01f8,
     0x03f8,0x03f4,0x03f8,0x01f8,0x01f8,
     0x01f8,0x01fc,0x01fc,0xe1ff,0xe0ff,
     0xc03f,0xc017,0xc003,0x2000,0x0000,
     0x0000,0x0000,0xe000,0xe400,0xfe00,
     0xff01,0xff01,0xffc3,0x1fc3,0x0fe3,
     0x07e3,0x03e1,0x03e0,0x03e1,0x03e0,
     0x03e0,0x03e0,0x87c0,0xc7c2,0xff03,
     0xff03,0xfe03,0xfe03,0x3800,0x0000,
     0x0000,0x0000,0x0030,0x1ff8,0x3fff,
     0xffff,0xffff,0xff9f,0xfb01,0xf800,
     0xfc00,0xfec0,0xfffc,0xfffe,0x7fff,
     0x07ff,0x07ff,0x001f,0xc01f,0xfe1f,
     0xffff,0xfff9,0xfff8,0x4cfc,0x0000,
     0x0000,0x0000,0x0002,0x017f,0x81ff,
     0xc1ff,0x81ff,0x81fc,0x83fc,0x01f8,
     0x01f0,0x01fc,0x01ff,0x01ff,0xc3ff,
     0xe3fc,0x83fc,0xc1f8,0xc1f8,0x81fc,
     0x83ff,0x81ff,0x01ff,0x001c,0x0000,
     0x0000,0x0000,0x0000,0xfc23,0xfc3f,
     0xfc3f,0xfc3f,0x003f,0x003f,0x003f,
     0x001f,0x3c7f,0xfc3f,0xf83f,0xfc3f,
     0x203f,0x003f,0x007f,0x007f,0x643f,
     0xfc3f,0xfc3f,0xfc6f,0x3c05,0x0000,
     0x0000,0x0000,0x1800,0xfb80,0xff80,
     0xfff0,0xfff0,0xb7f8,0x13f8,0x03f0,
     0xcbf0,0x9fe0,0xfff0,0xffe0,0xffc0,
     0xffc0,0xf7e0,0x17e0,0x07f0,0x07e0,
     0x07f0,0x03f0,0x83f8,0x8378,0x0000,
     0x0000,0x0000,0x0000,0x0000,0x0000,
     0x0000,0x0000,0x0000,0x0000,0x0000,
     0x0000,0x0000,0x0000,0x0000,0x0000,
     0x0000,0x0000,0x0000,0x0000,0x0000,
     0x0000,0x0000,0x0000,0x0000,0x0000},   // loser
    {0x0000,0x0000,0x0000,0x0000,0x0000,
     0x0000,0x0000,0x0001,0x0001,0x0001,
     0x0001,0x0003,0x0003,0x0003,0x0003,
     0x0003,0x0003,0x0001,0x0000,0x0000,
     0x0000,0x0000,0x0000,0x0000,0x0000,
     0x0000,0x0000,0x0000,0x0000,0x0ba0,
     0x7fe3,0xffe3,0xfee3,0xf023,0xe007,
     0xf007,0xf003,0xf003,0xf005,0xe003,
     0xf863,0xffc3,0xffc3,0x7fe7,0x2d61,
     0x0800,0x0000,0x0000,0x0000,0x0000,
     0x0000,0x0000,0x0000,0x0000,0x80c0,
     0xf0f8,0xe0f8,0xe1f8,0xf0f8,0xe0f8,
     0xfef8,0xfff8,0xfff8,0xfcf9,0xe0f1,
     0xe1f8,0xe0f8,0xe0f8,0xe0f8,0xd0a0,
     0x0000,0x0000,0x0000,0x0000,0x0000,
     0x0000,0x0000,0x0000,0x0000,0x5cf0,
     0xffe0,0xffe0,0xf980,0xfc00,0xf800,
     0xf040,0xffe0,0xffe0,0xfbe0,0xf801,
     0xf801,0xf801,0xffe3,0xffe3,0xffe3,
     0x0000,0x0000,0x0000,0x0000,0x0000,
     0x0000,0x0000,0x0000,0x0000,0x2aa0,
     0x3fe0,0x3fe0,0x3fe0,0x7ff0,0x7df0,
     0xf9f0,0xf9f0,0xf8f0,0xfdf8,0xfff8,
     0xfff8,0xfffc,0xf2fc,0xe07e,0xa068,
     0x0040,0x0000,0x0000,0x0000,0x0000,
     0x0000,0x0000,0x0000,0x0010,0x7ffe,
     0x7ffe,0xfffe,0x47e6,0x07e0,0x03e0,
     0x03e0,0x03e0,0x03e0,0x03e0,0x07e0,
     0x03e0,0x03e0,0x03c0,0x03e0,0x0560,
     0x0000,0x0000,0x0000,0x0000,0x0000,
     0x0000,0x0000,0x0000,0x0000,0x0b9c,
     0x1ffc,0x1ffc,0x1f20,0x1f00,0x1e00,
     0x1e08,0x1ffc,0x1ffc,0x3f70,0x3f00,
     0x3e00,0x1e00,0x3ffc,0x3ffc,0x1ffc,
     0x0000,0x0000,0x0000,0x0000,0x0000,
     0x0000,0x0000,0x0000,0x0000,0x4390,
     0x7ff0,0x7ffc,0x7ffe,0x7cbe,0x7c3e,
     0x7efc,0x7ffe,0x7ffc,0x7ff8,0x7ffc,
     0x7c3c,0x7c3c,0x7c3c,0x7c3e,0x1c3f,
     0x0000,0x0000,0x0000,0x0000,0x0000,
     0x0000,0x0000,0x0000,0x0000,0x0000,
     0x0000,0x0000,0x0000,0x0000,0x0000,
     0x0000,0x0000,0x0000,0x0000,0x0000,
     0x0000,0x0000,0x0000,0x0000,0x0000,
     0x0000,0x0000,0x0000,0x0000,0x0000},   // cheater
    {0x0000,0x0027,0x007f,0x007f,0x007e,
     0x003f,0x003f,0x001f,0x003f,0x001f,
     0x001f,0x001f,0x001f,0x000f,0x000f,
     0x001f,0x000f,0x000f,0x000f,0x000f,
     0x000f,0x000f,0x000c,0x0000,0x0000,
     0x0000,0x0080,0x0184,0x0787,0x0f07,
     0x1f07,0x1f0f,0x1f8f,0x1f8f,0x9f8f,
     0x9f87,0x9f87,0xfff7,0xffff,0xffff,
     0xfdff,0xfdff,0xf8ff,0xf1ff,0xf1ff,
     0xf0ff,0x70f7,0x1071,0x0010,0x0000,
     0x0000,0x401c,0xd1fc,0xf1fc,0xe1f8,
     0xe1f8,0xe1fc,0xc1f8,0xc3f8,0xc3f8,
     0xc3fc,0xc3fc,0xc1fc,0x81fc,0x82f8,
     0x83f0,0x01f8,0x01f8,0x01f0,0x03f8,
     0x03fc,0x03fc,0x0020,0x0000,0x0000,
     0x0000,0x0000,0x7bc3,0x7fc3,0x3fc3,
     0x3fc3,0x1fe1,0x3ff3,0x3ffb,0x3ffb,
     0x3ffb,0x1fff,0x1f7f,0x1f3f,0x3f9f,
     0x3f9f,0x3f8f,0x3f8f,0x7f83,0x3f87,
     0x3f85,0x3f81,0x1201,0x0000,0x0000,
     0x0000,0x0000,0xd8f7,0xf8ff,0xf87f,
     0xf87f,0xf03f,0xf87f,0xf87f,0xf87f,
     0xf07f,0xf83f,0xf03e,0xf83e,0xf87f,
     0xf87f,0xf87f,0xf07f,0xf0ff,0xf07f,
     0xf07f,0xd87f,0x5824,0x0000,0x0000,
     0x0000,0x0000,0x87b0,0x87f0,0x87f0,
     0x87f0,0xc3e0,0xe7f0,0xf7f0,0xf7f0,
     0xf7e0,0xfff0,0xffe0,0x7ff1,0x3ff0,
     0x3ff0,0x1ff0,0x1fe0,0x07e0,0x0fe0,
     0x0be0,0x03b0,0x02b0,0x0000,0x0000,
     0x0000,0x0000,0xbfff,0x7ffe,0x7ffe,
     0x7ffe,0x7e00,0xff00,0xfe00,0xfc00,
     0xfe0e,0x7ffe,0x7ffe,0xfffe,0xfe18,
     0xfe00,0xfe00,0xfc00,0x7f3a,0xfffe,
     0xffff,0xffff,0x0f0e,0x0000,0x0000,
     0x0000,0x0006,0x08fe,0x0fff,0x0fff,
     0x0fff,0x0fed,0x0fc4,0x0fc0,0x07f2,
     0x1fe7,0x0fff,0x0fff,0x0fff,0x0fff,
     0x0ffd,0x1fc5,0x1fc1,0x0fc1,0x0fc1,
     0x0fc0,0x1be0,0x0160,0x0000,0x0000,
     0x0000,0x0000,0xe000,0xe000,0xfc00,
     0xfc00,0xfe00,0xfe00,0xfc00,0xfc00,
     0xf800,0xfc00,0xf800,0xf000,0xf000,
     0xf800,0xf800,0xfc00,0xf800,0xfc00,
     0xfc00,0xfe00,0xde00,0x0000,0x0000}    // winner
};

/* Display String Literals */

static const char *intro[] = {
    "Start New Game",
    "Setup Game Options",
    "Exit the Game",
    "Copyright (C) 2001 John David Ratliff",
    "http://www.technoplaza.net",
    "Speed: ",
    "Level: ",
    "Return to the Main Menu"
};

static const char *speeds[] = {
    "Very Slow","Slow","Medium Slow","Leisurely","Medium",
    "Average","Average Plus","Quick","Fast","Very Fast"
};

static const char *difficulties[] = {"Easy","Normal","Hard"};

#endif

As we have seen in some other programs, we need to make slightly different versions for the TI-89 and TI-92+/V200. So we first need to turn off the calculators in the Project, Options, Compilation Tab, Program Options, Calculators Tab. Uncheck all three.

Now, if you are compiling for the TI-92+/V200, comment out the USE_TI89 definition at the top of nibbles.h. If you are building for the TI-89, no change is necessary. Build the project and send it to TiEmu. It will look something like this:

TI-89 AMS 2.08 nibbles.89z

 

Continue with the Program Analysis in Part II

 

Copyright © 1998-2007 Techno-Plaza
All Rights Reserved Unless Otherwise Noted

Get Firefox!    Valid HTML 4.01!    Made with jEdit