IBM®
Skip to main content
    Country/region [select]      Terms of use
 
 
    
     Home      Products      Services & solutions      Support & downloads      My account     
Testing and measuring the TAMS 3011, Part 4: Interfacing with flash and avoiding file systems
skip to main content

developerWorks  >  Power Architecture technology  >

Testing and measuring the TAMS 3011, Part 4: Interfacing with flash and avoiding file systems

Would you believe... embedded Frotz!

developerWorks
Document options

Document options requiring JavaScript are not displayed


Rate this page

Help us improve this content


Level: Intermediate

Peter Seebach (developerworks@seebs.plethora.net), Freelance author, Plethora.net

30 May 2006

The Z-machine interpreter is a benchmark for OS functionality and development environments. With the groundwork of porting curses accomplished in Part 3, this article shows you how to complete the Z-machine interpreter, setting it up to use flash memory to save state, without the newfangled luxury of a file system.

The Z-machine is a virtual machine with the unusual design requirement that it be able to be emulated on a Commodore 64, an Apple II, and an early 8088-based DOS machine. The initial goal was to allow games to be written once and run on multiple machines. The games don't need (and can't have) a great deal of volatile storage, but do have to be able to access a fairly large amount of raw text. Later versions added some minimal graphics support, but the fundamental goal is to run text adventures.

While you might think this would make the program utterly trivial, it is less trivial than it appears. The Z-machine has cursor addressing abilities, can (in theory) use both fixed-width and proportional fonts, and can even perform timed actions in response to user input.

My favorite Z-machine interpreter, and the one I generally use when testing a development platform, is Frotz. Frotz is reasonably straightforward, running with essentially no effort on most UNIX®-like systems.

Having ported the curses library to eCos (see the previous article), it's time to start on Frotz.

Getting the source

The Frotz distribution (see Resources) contains a few subdirectories for source; the only ones important for our purposes are the common/ and curses/ subdirectories, containing the generic Frotz code and the interface code for UNIX systems with curses, respectively. Getting the code to compile is fairly trivial; a couple #defines are missing, but that's nothing to break a sweat over.

However, this leaves you with a program which will do nothing but print a usage message. After all, there are no command line arguments to tell it which file to load, and indeed, no file system to load the file from. This is a common issue encountered when porting application software from a desktop environment into an embedded one. Furthermore, the program performs sanity checks (it requires the HOME environment variable to exist, and refuses to run as root), which do not fit the embedded environment, and must be removed.

Here you face a design decision: should you use a memory device to load the file, or embed it in the executable? For now, embedding the story file in the executable is simpler. This defeats the elegant design of a standard interpreter program for story files, but fits well with the single executable image design favored by eCos.

The code to manipulate the story file is found in fastmem.c, mostly in the init_memory function. So, the cheap thing to do is write another file containing

	unsigned char game[] = {
	...
	};

and fill it in with the contents of an actual game file, and then include this file in fastmem.c. Because the file is internal, you can omit the logic for calculating the size of a story file; you already know how large it is. Replacing a couple of calls to fread() with calls to memcpy() gets you a working Frotz executable which, when loaded on the 3011, can play Zork. This isn't the only remaining reference to the file system. One reference, easily fixed, is the re-read of the story file on a restart. That's no problem; just replace it with memcpy. Loading and saving, though...



Back to top


Loading and saving

Frotz writes to files in a few cases. You can save transcripts of sessions and can save and later play back lists of commands. These features, while excellent, are hard to apply to an embedded appliance. What would be fairly useful, though, is support for loading and saving games.

The save format Frotz uses merits mention in passing, because it's an instance of the IFF file format, which I still think is the world's best file format; more on this in a bit. What matters, for now, is finding some way to store some data. First off, you need to create a partition in the flash structure, just so you know where to store your data. In RedBoot, you do this with the "fis" command. The exact layout of existing files in flash can vary. I went with:

	fis create -b 0x0 -f 0xc3800000 -l 0x20000 save

The options specify, in order, the memory location of the data to store in flash, the address in the flash memory block, and its length. The flash memory on the 3011 has the address range 0xc0000000 through 0xc4000000. Normally, this would be used to store an image that's already been loaded into memory, but in this case, it's just a dummy region that the program will use.

At this point, it's time to start storing data. You might think that, because there's a memory address associated with this, it could just be read and written from memory; in fact, you have to use the flash driver.

The flash driver

The eCos flash driver exists primarily to serve the needs of RedBoot. It's not reentrant, and it won't handle interruptions well. You can't specify a particular flash device; when you initialize the driver, it finds the flash device on the system and uses it. Conveniently, since there's usually only one, that's a very suitable interface. The flash driver provides a somewhat abstracted interface to flash memory; for instance, it hides the ECC management required by the NAND flash on the TAMS 3011.

The obvious interface consists of flash_init(), used to initialize the flash driver, flash_read(), used to read chunks of data from flash memory, and flash_program(), used to write data to flash memory. In fact, this isn't quite everything; before you write to flash memory, you must erase it, using flash_erase(). (I omitted this step during development and ended up with a program which would get ECC errors trying to read save files.)

The flash_init function takes a single argument, a pointer to a printf-like function used for diagnostic messages. One obvious choice would be the diag_printf function used for diagnostics. Using this is a good choice during development, because it can be used to get crucial error messages.

After the flash area is initialized, you can read or write blocks of it. In practice, a save file for a Z-machine game needs at most 64k for modifiable data, plus stack storage. In short, it will definitely fit within 128kB of storage, which is what was allocated above. Even with the overhead introduced by the file format used for saves, 128kB is plenty.

Reading data is trivial; the flash_read() function is not much more complicated than memcpy() to use, although it takes an extra parameter for reporting the addresses of any errors. The flash_program() function is similar; the only quirk is that you have to erase a block before programming it.

Save file formats and stdio

However, it would be well beyond foolhardy to simply flash each byte of memory. What you want is a buffer to read and write from. The code used for save and restore in Frotz already had #defines for a pair of functions named put_c and get_c; I extended this to include an f_tell and f_seek function and replaced the trivial macros with functions manipulating a simplistic file structure:


Listing 1. File manipulation

	typedef struct {
		unsigned char *space;
		size_t pos;
	} MFILE;

	int get_c(MFILE *m);
	int put_c(int, MFILE *m);
	int f_seek(MFILE *m, int, int);
	size_t f_tell(MFILE *m);

This interface, with tiny little functions (only f_seek is more than two lines of code), is enough of a subset of stdio to allow for completely trivial conversion of the save file code in Frotz to read and write to allocated chunks of memory. Before the restore routine is started, the chunk of flash defined from RedBoot is read in:

	flash_read((void *) SAVE_ADDR, gfp->space, SAVE_SIZE, &v);

On the saving end, after the save routine has filled in a block of memory, the memory can be written out to flash:

        flash_erase(SAVE_ADDR, SAVE_SIZE, &v);
        flash_program(SAVE_ADDR, gfp->space, SAVE_SIZE, &v);

The save format Frotz uses, called Quetzal, is a standardized save format shared by a number of Z-machine interpreters. The Quetzal format is actually an instance of the venerable IFF file format, and is exceedingly flexible. It also, delightfully enough, records the length of the file in a header, so that the restore routine doesn't rely on getting an EOF; this means you can just hand the routine the full 128k buffer and let it read what it wants. The IFF file format is rarely seen these days, but it remains one of the best combinations of flexibility and feature set, and it made the porting task easier here.

In the process of adding this, I either commented out or simply removed references to file names, because this particular implementation doesn't really provide for files; it just provides for a single save slot.



Back to top


New features, new bugs

In fact, the restore feature didn't work right away. Z-machine games contain up to 64k of modifiable memory; the starting values for this memory are simply part of the save file. The compressed save format (the default, because it's more efficient) works by saving only bytes which are different from those in the story file. My initial design, having copied the game into memory, made a MFILE structure which pointed at the in-memory copy. This meant that, when it came time to save, the current in-memory copy was compared to the current in-memory copy, and only differences were stored. Saving the game produced a save file indicating that nothing had changed yet, so restoring the save file had no effect. Making a spare copy of the story file resolved this.

A more subtle bug is that the curses library is easily confused if the cursor is moved by any other function, for instance, the diag_printf() calls made by the flash library. You can't just pass a null function pointer, but you could pass a dummy function that doesn't do anything:

	int
	say_no_evil(const char *fmt, ...)
	{
		return 0;
	}

This resolves the problem, and save and restore both work now. Since the game doesn't need to ask for a file name, the operations are essentially instant; the only immediate feedback is that the status bar updates, and the game prints "Ok."

One feature of the Z-machine that's not used in Zork, but is in some other games, is the ability to have character input time out. This feature isn't very common, because text adventures generally eschew timed events; these are thinking games, not shooters. However, it's certainly possible to make use of this. I picked a game from the 2002 interactive fiction competition which uses the feature, to test it out. The game is called Janitor (see Resources), and it uses the routine for a few "cutscenes" in which the game inputs characters automatically. The test was utterly trivial; as expected, it worked on the first try. (As a coauthor of Janitor, I was greatly relieved.)



Back to top


Other surprises and quirks

This article hasn't always covered the detailed process by which I learned how to do things. There are gaps in the eCos documentation, and you have to be willing to poke around a bit to find things sometimes. The version of eCos I used for this was actually rather old: September 2005. I spent a good two hours debugging the lack of gettimeofday(), only to discover that it's been moved into the place I looked first since the release I was working with.

You cannot always easily figure out which module contains a given feature you're looking for. In practice, you mostly don't need to. You can simply use one of the more complete templates (such as the "net" template) and trust the linker to strip out unneeded features. This will work for most users. The majority of systems have plenty of free space, and unused code is dropped at link time.

The command-line ecosconfig tool doesn't give full control over fine-grained tweaks; to do that, you need to either use the graphical configuration tool (configtool, which is a Tcl application) or edit files by hand. Editing files by hand involves learning to read the CDL file format, which is fully documented in the eCos reference manual. If you're coming to eCos for the first time, allow a bit of time to get familiar with the tools.

Coming up next: Having looked at porting applications to eCos, I'll move on to the RedBoot boot loader, which is itself an eCos application. RedBoot has been used as a host environment for other OSes, and it offers many of the sorts of features (loading files over tftp, manipulating flash) that people often wish a bootloader had. A more detailed look at its capabilities and design is perhaps in order; join us then.



Resources

Learn

Get products and technologies

Discuss


About the author

Peter Seebach

Peter Seebach thinks 64k is enough for anyone.




Rate this page


Please take a moment to complete this form to help us better serve you.



YesNoDon't know
 


 


12345
Not
useful
Extremely
useful
 


Back to top



    About IBMPrivacyContact