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

     Introduction to C

     Keyboard Input

     Graphics Intro

     Slider Puzzle 1

     Functions

     Pointers

     Dynamic Memory

     Slider Puzzle 2

     Structures

     Bit Manipulation

     Advanced Pointers

     File I/O

       Part I

       Part II

       Part III

       Part IV

     Graduate Review

   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.

Lesson 10: File I/O

Step 1 - Introduction to File I/O

One of the more important concepts later in programming is the ability to store information even after the program has terminated. This technique has many uses, such as saving hi score information or level data for a game.

Storing data outside the program has many advantages: small program size, modularity, persistent data retrieval. For example, imagine if you had a one player game, and that game saved your name along with your score. Now, each time you start the game, you must enter your name, because the program doesn't remember things like this.

Well, what could we do to solve this program. Well, we could use a global variable whose value is embedded inside the program. That's probably not a bad idea for small sets of data, but what about giant sets of data? We don't want to add 4K to our program size, especially on a system whose memory is tight enough as it is. Furthermore, we have the added limitation that all programs on the TI-89/92+/V200 must be smaller than 64K. This is the largest size a program can be. It's a limitation of the system. Even if we compress the program, the program must be smaller than 64K when decompressed. However, we can load as much data as we have space for into memory once our program is started. Finally, we can take advantage of the fact that our FlashROM space is readable, but not executable. So, we cannot run programs in this space1, but we can store data there.

Okay, we know that we can store data outside the program, and we know that we have lots of extra space outside the program, how do we take advantage of that. File I/O of course. Instead of storing data inside the program file, we will create another file in which to store data. Thanks to Zeljko Juric, we will soon see how easy this is.

1. Programs that are executed from FlashROM are actually copied into our RAM memory and run from there, then deleted once the program stops running.

Step 2 - A Short Example Using File I/O

Start TIGCC and begin a new project. Name the source file fileio and save the project. Edit the file to look like this:

fileio.c


// comment this next line out for TI-92+/V200 support
#define USE_TI89

#ifndef USE_TI89
    #define USE_TI92PLUS
    #define USE_V200
#endif

#include <dialogs.h>
#include <ctype.h>
#include <string.h>
#include <mem.h>
#include <stdio.h>
#include <kbd.h>
#include <stdlib.h>
#include <alloc.h>
#include <args.h>
#include <menus.h>

// the minus and the negative sign have different values, though they look nearly identical
#define DASH    173

#ifdef USE_TI89
    enum DialogConstants    {DLG_WIDTH = 140, DLG_HEIGHT = 85};
#else
    enum DialogConstants    {DLG_WIDTH = 200, DLG_HEIGHT = 90};
#endif

enum ErrorConstants {FILEIO1,FILEIO2,FILEIO3,FILEIO4,FILEIO5,MEMERR1,MEMERR2,
             DATA_ERROR,DOB_ERROR,HEIGHT_ERROR,WEIGHT_ERROR};
enum MenuConstants  {MENU_DATA = 1000,MENU_READ,MENU_WRITE,MENU_PRINT,MENU_EXIT};

const char *error[] = {"File I/O Error","Unable to Open File!",
            "Unable to read or write data to file",
            "File I/O Status","Success!",
            "Memory Error","Not enough free memory!",
            "Data Error","DOB Must be MM-DD-YYYY",
            "Height must be between 100 and 250 cm",
            "Weight must be between 35 and 180 kg"};

typedef struct {
    char name[15];
    short int height;
    short int weight;
    char dob[11];
} PERSON;

inline void dlgError(short int title, short int msg) {
    DlgMessage((char *)error[title],(char *)error[msg],BT_OK,BT_NONE);
}

// check for valid date of birth (MM-DD-YYYY)
short int isValidDOB(const unsigned char *dob) {
    if (isdigit(dob[0]) && isdigit(dob[1]) && (dob[2] == '-' || dob[2] == DASH) &&
        isdigit(dob[3]) && isdigit(dob[4]) && (dob[5] == '-' || dob[5] == DASH) &&
        isdigit(dob[6]) && isdigit(dob[7]) && isdigit(dob[8]) && isdigit(dob[9])) {

        return TRUE;
    }

    return FALSE;
}

// check for valid height (in centimeters)
short int isValidHeight(const short int height) {
    if (height > 100 && height < 250) {
        return TRUE;
    }

    return FALSE;
}

// check for valid weight (in kilograms)
short int isValidWeight(const short int weight) {
    if (weight > 35 && weight < 180) {
        return TRUE;
    }

    return FALSE;
}

void initPerson(PERSON *p) {
    // terminate the person strings
    p->name[0] = 0;
    p->dob[0] = 0;

    // enter generic person information
    strcpy(p->name,"Dominic Silver");
    strcpy(p->dob,"06-02-1972");
    p->height = 190;
    p->weight = 87;
}

void formatRequestString(char *temp, PERSON *p) {
    // erase the buffer string
    memset(temp,0,34*sizeof(char));

    // format the buffer string so the dialog box will have default values
    sprintf(temp,"%-15s%-11s%03hd %03hd",p->name,p->dob,p->height,p->weight);

    // add string separators
    temp[14] = 0;
    temp[25] = 0;
    temp[29] = 0;
    temp[33] = 0;
}

short int getData(PERSON *p, char *buffer) {
    HANDLE dlg = H_NULL;
    int done = FALSE;
    char *token;

    // create the dialog box
    if ((dlg = DialogNewSimple(DLG_WIDTH,DLG_HEIGHT)) != H_NULL) {
        // format the dialog box
        DialogAddTitle(dlg,"Personal Information",BT_OK,BT_NONE);
        DialogAddRequest(dlg,5,15,"Name:",0,14,18);
        DialogAddRequest(dlg,5,25,"DOB:",15,10,18);
        DialogAddRequest(dlg,5,35,"Height (cm):",26,3,5);
        DialogAddRequest(dlg,5,45,"Weight (kg):",30,3,5);

        while (!done) {
            // loop until the user presses ENTER
            while (DialogDo(dlg,CENTER,CENTER,buffer,NULL) != KEY_ENTER);

            // grab the name from the string buffer
            token = buffer;
            p->name[0] = 0;
            strcpy(p->name,token);

            // grab the DOB from the string buffer
            token = buffer + 15;
            p->dob[0] = 0;
            strcpy(p->dob,token);

            // grab the height from the string buffer
            token += 11;
            p->height = atoi(token);

            // grab the weight from the string buffer
            token += 4;
            p->weight = atoi(token);

            // we're done unless we fail one of our validity tests
            done = TRUE;

            // check for valid DOB entry (MM/DD/YYYY)
            if (!isValidDOB((const unsigned char *)p->dob)) {
                dlgError(DATA_ERROR,DOB_ERROR);
                done = FALSE;
            }

            // check for reasonable valid height
            if (!isValidHeight((const short int)p->height)) {
                dlgError(DATA_ERROR,HEIGHT_ERROR);
                done = FALSE;
            }

            // check for reasonable valid weight
            if (!isValidWeight((const short int)p->weight)) {
                dlgError(DATA_ERROR,WEIGHT_ERROR);
                done = FALSE;
            }
        }

        // free the memory used by the dialog
        HeapFree(dlg);
    } else {
        dlgError(MEMERR1,MEMERR2);
        return FALSE;
    }

    return TRUE;
}

short int writeData(PERSON *p) {
    FILE *f = NULL;
    short int fileio = TRUE;

    // open file for writing
    if ((f = fopen("TESTFILE","wb")) == NULL) {
        dlgError(FILEIO1,FILEIO2);
        fileio = FALSE;
    } else {
        // write structure data to the file
        if (fwrite(p,sizeof(PERSON),1,f) != 1) {
            dlgError(FILEIO1,FILEIO3);
            fileio = FALSE;
        }

        // append the file ID tag
        fputc(0,f);
        fputs("FIO",f);
        fputc(0,f);
        fputc(OTH_TAG,f);

        // close the file
        fclose(f);
    }

    return fileio;
}

short int readData(PERSON *p) {
    FILE *f = NULL;
    short int fileio = TRUE;

    // open file for reading in binary mode
    if ((f = fopen("TESTFILE","rb")) == NULL) {
        dlgError(FILEIO1,FILEIO2);
        fileio = FALSE;
    } else {
        // read data from file into PERSON structure
        if (fread(p,sizeof(PERSON),1,f) != 1) {
            dlgError(FILEIO1,FILEIO3);
            fileio = FALSE;
        }

        // close the file
        fclose(f);
    }

    return fileio;
}

void printData(PERSON *p) {
    char name[25],dob[20],height[20],weight[20];
    HANDLE dlg = H_NULL;

    if ((dlg = DialogNewSimple(DLG_WIDTH,DLG_HEIGHT)) != H_NULL) {
        // create the personal information strings
        sprintf(name,"Name: %s",p->name);
        sprintf(dob,"DOB: %s",p->dob);
        sprintf(height,"Height: %hd cm",p->height);
        sprintf(weight,"Weight: %hd kg",p->weight);

        // format the dialog box
        DialogAddTitle(dlg,"Personal Information",BT_OK,BT_NONE);
        DialogAddText(dlg,5,15,name);
        DialogAddText(dlg,5,25,dob);
        DialogAddText(dlg,5,35,height);
        DialogAddText(dlg,5,45,weight);

        // display the dialog box
        DialogDo(dlg,CENTER,CENTER,NULL,NULL);

        HeapFree(dlg);
    }
}

void _main(void) {
    char buffer[34];
    PERSON p;
    short int done = 0, option = MENU_DATA;
    HANDLE dlg = H_NULL, menu = H_NULL;

    // initialize the person structure
    initPerson(&p);

    // format the request buffer string
    formatRequestString(buffer,&p);

    if ((dlg = DialogNewSimple(DLG_WIDTH,DLG_HEIGHT)) == H_NULL) {
        dlgError(MEMERR1,MEMERR2);
        return;
    }

    if ((menu = PopupNew(NULL,0)) == H_NULL) {
        dlgError(MEMERR1,MEMERR2);
        HeapFree(dlg);
        return;
    }

    // create Dialog Menu
    PopupAddText(menu,-1,"Enter new Data",MENU_DATA);
    PopupAddText(menu,-1,"Read Data from File",MENU_READ);
    PopupAddText(menu,-1,"Save Data to File",MENU_WRITE);
    PopupAddText(menu,-1,"Print Information",MENU_PRINT);
    PopupAddText(menu,-1,"Exit",MENU_EXIT);

    DialogAddTitle(dlg,"Main Menu",BT_OK,BT_CANCEL);
    DialogAddPulldown(dlg,5,15,"Selection:",menu,0);

    do {
        if (DialogDo(dlg,CENTER,CENTER,NULL,&option) != KEY_ENTER) {
            option = MENU_EXIT;
        }

        switch (option) {
            case MENU_DATA:
                getData(&p,buffer);
                printData(&p);
                break;
            case MENU_READ:
                if (readData(&p)) {
                    dlgError(FILEIO4,FILEIO5);
                    formatRequestString(buffer,&p);
                    printData(&p);
                }
                break;
            case MENU_WRITE:
                if (writeData(&p)) {
                    dlgError(FILEIO4,FILEIO5);
                }
                break;
            case MENU_PRINT: printData(&p); break;
            case MENU_EXIT: done = TRUE; break;
        }
    } while (!done);

    // free the memory used by the dialog box and selector menu
    HeapFree(menu);
    HeapFree(dlg);
}

Step 2a - Compile and run the Program

Before we build this example, we need to make an adjustment to the program options. We did this once before in lesson 3 to set the MIN_AMS version. Goto Project, Options, Compilation Tab, Program Options, Calculator. Deselect all three calculators. The calculator build selection is taken care of with the USE_TI89 or USE_TI92PLUS USE_V200 definitions.

Build the binary for your calculator. Note that you will need to comment out the top #define USE_TI89 line if you are compiling the TI-92+/V200 version. Due to differences in screen size and fonts used in dialog boxes, I had to make separate programs. The programs are identical except for dialog box sizes. Send the file to TiEmu and run it. It will look something like this:

TI-89 AMS 2.05 fileio-89z.gif TI-92+ AMS 2.05 fileio-9xz.gif

Continue with Part II

 

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

Get Firefox!    Valid HTML 4.01!    Made with jEdit