pnakada@oracle.com (Paul Nakada) (04/14/90)
#! /bin/sh
# This is a shell archive. Remove anything before this line, then feed it
# into a shell via "sh file" or similar. To overwrite existing files,
# type "sh file -c".
# The tool that generated this appeared in the comp.sources.unix newsgroup;
# send mail to comp-sources-unix@uunet.uu.net if you want that tool.
# If this archive is complete, you will see the following message at the end:
# "End of archive 2 (of 2)."
# Contents: Makefile apple.h main.c mega2.c
# Wrapped by pnakada@pnakada on Thu Apr 12 00:46:04 1990
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'Makefile' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'Makefile'\"
else
echo shar: Extracting \"'Makefile'\" \(1187 characters\)
sed "s/^X//" >'Makefile' <<'END_OF_FILE'
X#
X#Makefile for Apple ][ Emulator
X#(C) 1989 Ben Koning [556498717 408/738-1763 ben@apple.com]
X#
X
X#Local printing command:
XPRINT = lpr -Psimplex
X
X#Apple.II is split up as follows:
XOBJ = main.o 6502.o mega2.o debug.o
X
X#Make normal version:
Xdebug: ${OBJ} apple.h
X cc -g $(OBJ) -lcurses -ltermcap
Xlint:
X lint main.c 6502.c mega2.c debug.c -lcurses -ltermcap
X
X#Make final optimized version:
Xfinal:
X touch apple.h
X make debug
X strip a.out
X mv a.out Apple.II
X make clean
X
X#Make profiling version:
Xprofil:
X cc -pg main.c 6502.c mega2.c debug.c -lcurses -ltermcap
X
X#Building modules from source:
Xmain.o: main.c apple.h
X cc -c -g main.c
X6502.o: 6502.c apple.h
X cc -c -g 6502.c
Xmega2.o: mega2.c apple.h
X cc -c -g mega2.c
Xdebug.o: debug.c apple.h
X cc -c -g debug.c
X
X#Cleanup of directory:
Xclean:
X rm -f ${OBJ}
X rm -f file
X rm -f a.out
X rm -f core
X
X#Printout without long ROM listings:
Xprint:
X rm -f file
X touch file
X pr README >> file
X pr Makefile >> file
X pr *.h >> file
X pr main.c >> file
X pr 6502.c >> file
X pr mega2.c >> file
X $(PRINT) file
X rm -f file
X wc *.h *.c
X
X#Printout with long copyrighted ROM listings:
Xprintall:
X make print
END_OF_FILE
echo shar: NEWLINE appended to \"'Makefile'\"
if test 1188 -ne `wc -c <'Makefile'`; then
echo shar: \"'Makefile'\" unpacked with wrong size!
fi
# end of 'Makefile'
fi
if test -f 'apple.h' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'apple.h'\"
else
echo shar: Extracting \"'apple.h'\" \(1242 characters\)
sed "s/^X//" >'apple.h' <<'END_OF_FILE'
X
X/*
X *apple.h -- Globals' xdefs and equates for system calls for Apple ][ Emulator
X *(C) 1989 Ben Koning [556498717 408/738-1763 ben@apple.com]
X */
X
X
X
X/* Character which ends the emulation: */
X
X#define MEGAQUITKEY 001 /* Control-A */
X
X
X
X/* XENIX/BSD - Compatible includes: */
X
X#ifndef BUFSIZ
X#include <stdio.h>
X#endif
X
X#ifndef isalpha
X#include <ctype.h>
X#endif
X
X#ifndef CBREAK
X#include <sgtty.h>
X#endif
X
X#ifndef TIOCGETP
X#include <sys/ioctl.h>
X#endif
X
X#ifndef O_NDELAY
X#include <fcntl.h>
X#endif
X
X#define BYTE unsigned char
X#define ADDR int
X
X
X
X/* 6502 Globals: */
X
Xextern int A,X,Y,P,S;
Xextern ADDR PPC;
X
X
X
X
X
X/* Emulation Globals: */
X
Xextern BYTE MMemory[];
Xextern BYTE Rom[];
Xextern BYTE MRam[];
Xextern BYTE MRam1[];
Xextern BYTE MRam2[];
Xextern BYTE RamRead;
Xextern BYTE RamWrite;
Xextern BYTE Bank2Enable;
X
Xextern BYTE MegaRand;
Xextern BYTE MegaLastKey;
Xextern BYTE MegaQuitDetect;
Xextern int DebugSingle;
Xextern int DebugTrace;
Xextern ADDR DebugBreak;
X
X
X/* Apple ROM Contents: */
X
Xextern BYTE MegaGetMem();
X
X
X/* Termcap stuff: */
X
X
Xextern void Debugger();
Xextern void MegaShutDown();
Xextern void MegaStartUp();
Xextern void MegaPutMem();
X
Xextern void prodos();
X
Xextern void CPUShutDown();
Xextern void CPUReset();
Xextern void CPUExecute();
END_OF_FILE
echo shar: NEWLINE appended to \"'apple.h'\"
if test 1243 -ne `wc -c <'apple.h'`; then
echo shar: \"'apple.h'\" unpacked with wrong size!
fi
# end of 'apple.h'
fi
if test -f 'main.c' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'main.c'\"
else
echo shar: Extracting \"'main.c'\" \(2227 characters\)
sed "s/^X//" >'main.c' <<'END_OF_FILE'
X
X/*
X *main.c -- Globals and emulation setup for Apple ][ Emulator
X *(C) 1989 Ben Koning [556498717 408/738-1763 ben@apple.com]
X */
X
X#include "apple.h"
X#include <curses.h>
X#define REF_DELAY 2000
X
Xmain (argc,argv)
X
Xint argc;
Xchar *argv [];
X
X{
X short int byte;
X int i, addr; /* Loop iterators */
X int refdelay = REF_DELAY;
X FILE *fp;
X char *D0FILE = "APPLESOFT.ROM",
X *F8FILE = "AUTOSTART.ROM" ;
X
X /* Interpret args: 'invokename [-t] [-m] [-i]' */
X for (i = 1; i <= argc-1; i++)
X {
X if (!strcmp (argv [i],"-t"))
X DebugSingle = 1;
X else
X if (!strcmp (argv [i],"-m"))
X F8FILE = "MONITOR.ROM";
X else
X if (!strcmp (argv [i],"-i"))
X D0FILE = "INTEGER.ROM";
X else
X {
X (void)fprintf (stderr,"Usage: %s [-t] [-m] [-i]\n",
X argv [0]);
X exit (1);
X }
X }
X
X /* Print banner message: */
X (void)fprintf (stderr,"%s: Apple ][ Emulation - Ben Koning - v1.0\n",
X argv [0]);
X
X (void)initscr();
X
X /*--------------------------------------------------*\
X Load the 'ROM' with the appropriate 6502 code:
X \*--------------------------------------------------*/
X fp = fopen (D0FILE, "r");
X for (addr = 0xd000; addr <= 0xf7ff; addr++)
X Rom [addr - 0xd000] = fgetc (fp);
X (void)fclose (fp);
X
X fp = fopen (F8FILE, "r");
X for (addr = 0xf800; addr <= 0xffff; addr++)
X Rom [addr - 0xd000] = fgetc (fp);
X (void)fclose (fp);
X
X /*--------------------------------------------------*\
X load prodos as $2000
X \*--------------------------------------------------*/
X fp = fopen ("PRODOS", "r");
X addr = 0x2000;
X while ((byte = fgetc (fp)) != EOF)
X MMemory [addr++] = byte;
X (void)fclose (fp);
X
X /* Make sure banner message is seen: */
X sleep (2);
X
X /* Initialize the emulation: */
X MegaStartUp ();
X CPUReset ();
X
X /*--------------------------------------------------*\
X Run it until user wants to quit:
X \*--------------------------------------------------*/
X while (!MegaQuitDetect)
X {
X CPUExecute ();
X if (!(--refdelay))
X {
X refresh();
X refdelay = REF_DELAY;
X }
X if (MegaQuitDetect || DebugSingle || DebugTrace)
X Debugger();
X }
X
X /* Exit cleanly: */
X MegaShutDown ();
X exit (0);
X}
X
X
END_OF_FILE
echo shar: NEWLINE appended to \"'main.c'\"
if test 2228 -ne `wc -c <'main.c'`; then
echo shar: \"'main.c'\" unpacked with wrong size!
fi
# end of 'main.c'
fi
if test -f 'mega2.c' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'mega2.c'\"
else
echo shar: Extracting \"'mega2.c'\" \(12594 characters\)
sed "s/^X//" >'mega2.c' <<'END_OF_FILE'
X/*
X *mega2.c -- Apple ][ soft switches->UNIX emulation for Apple ][ Emulator
X *(C) 1989 Ben Koning [556498717 408/738-1763 ben@apple.com]
X */
X
X#include "apple.h"
X#include <curses.h>
X
X
X/*
X * Termcap globals:
X */
X
X
X/*
X * Emulation globals:
X */
X
XBYTE MMemory [49152]; /* Main memory */
XBYTE Rom [12288]; /* 12K ROM bank of Main memory */
XBYTE MRam [12288]; /* 12K RAM bank of Main memory */
XBYTE MRam1 [4096]; /* Main bank 1 $D000-$DFFF */
XBYTE MRam2 [4096]; /* Main bank 2 $D000-$DFFF */
XBYTE RamRead = 0; /* set if 16K RAM readable */
XBYTE RamWrite = 0; /* set if 16K RAM writeable */
XBYTE Bank2Enable = 0; /* set if bank 2 Ram enabled */
X
XBYTE MegaRand = 0; /* Always contains 8-bit random number */
XBYTE MegaLastKey = 0; /* $C00X keyboard latch value */
XBYTE MegaQuitDetect = 0; /* Set if user requests to quit */
X
Xvoid PutC010();
Xvoid Put0400();
Xvoid GetC080();
XBYTE GetC000();
XBYTE GetC010();
XBYTE GetC030();
X
Xvoid ProInit();
Xvoid ProFormat();
Xvoid ProRead();
Xvoid ProWrite();
Xvoid ProStatus();
X
X/*
X * Base address table for 24 lines of text/lores page 1 ($400..$7F8);
X * 40 bytes for each line. Note that screen "holes" exist:
X */
X
Xint LBasCalc [24] =
X
X{
X1024,
X1152,
X1280,
X1408,
X1536,
X1664,
X1792,
X1920,
X1064,
X1192,
X1320,
X1448,
X1576,
X1704,
X1832,
X1960,
X1104,
X1232,
X1360,
X1488,
X1616,
X1744,
X1872,
X2000
X};
X
X#define MegaPutChar(c) addch(c)
X
X/**************************************************************************/
X/* Dispatch routines: The MegaPutMem and MegaGetMem, below, have default */
X/* behavior for I/O space. They may also contain calls to other routines */
X/* in this file which handle Apple II behavior. If these latter routines */
X/* need initialization/shutdown, that code should be placed in the two */
X/* MegaStartUp and MegaShutDown routines, below. */
X/**************************************************************************/
X
X#ifdef NEVER
Xvoid MegaPutChar (c)
Xchar c; /* This makes a function out of the "putchar" macro */
X
X{
X addch (c);
X}
X#endif
X
X/* This routine is called at emulation startup time. All initialization
X stuff, like setting terminal modes, opening files, etc. goes here. */
Xvoid MegaStartUp ()
X{
X register int i; /* Iterator */
X
X /* init the pseudo disk */
X ProInit();
X
X /* Set input modes on terminal: */
X noecho();
X raw();
X
X /* Fill 40-column screen with simulated initial memory pattern: */
X clear();
X for (i = 0; i <= 23; i++)
X mvaddstr (i, 0, "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@");
X
X refresh();
X
X /* Initialize some variables: */
X MegaQuitDetect = 0;
X MegaLastKey = 0;
X MegaRand = 0;
X}
X
X
X
X/* This routine is called at emulation shutdown time. All things
X started in MegaStartUp, above, should be cleaned up here. */
Xvoid MegaShutDown ()
X{
X /* Clear screen: */
X clear();
X refresh();
X
X /* Reset sane modes on terminal: */
X echo();
X noraw();
X endwin();
X}
X
X/* This routine handles ALL stores to the 64K address space, in order to
X faciliate special Apple ][ - specific effects. Not all behaviors may
X be implemented; to install a new one, install the call in this routine.
X Returns: Nothing. */
Xvoid MegaPutMem (addr, byte)
Xregister ADDR addr;
Xregister BYTE byte;
X{
X /* Make sure these are in range: */
X addr &= 0xffff;
X byte &= 0xff;
X
X /* Do random number generation: */
X MegaRand = (MegaRand + addr + byte) & 0xff;
X
X /* If in ROM, we are done here: */
X if (addr >= 0xd000 && !RamWrite)
X {
X/* Rom[addr - 0xd000] = byte; */
X return;
X }
X
X if (addr < 0xc000) /* Main memory */
X MMemory [addr] = byte;
X else if (addr >= 0xd000 && addr < 0xe000) /* 4K RAM Banks */
X {
X BYTE *bank = Bank2Enable ? MRam2 : MRam1;
X
X bank [addr - 0xd000] = byte;
X }
X else if (addr >= 0xe000) /* 8K RAM Bank */
X MRam [addr - 0xe000] = byte;
X else if ((addr >= 0xc010) && (addr <= 0xc01f)) /* soft switches */
X PutC010 (0);
X else if (addr >= 0xc080 && addr < 0xc090)
X GetC080 (addr);
X
X /* Now do the appropriate memory-mapped OUTPUT functions, if any: */
X if ((addr >= 0x0400) && (addr < 0x0800))
X Put0400 (addr, byte);
X}
X
X
X/* This routine handles ALL fetches from the 64K address space, in order to
X facilitate special Apple ][ - specific effects. Not all behaviors may
X be implemented; to install a new one, install the call in this routine.
X Returns: Value at location (could be random if I/O, etc.). */
XBYTE MegaGetMem (addr)
Xregister ADDR addr;
X{
X register BYTE data; /* Data from memory space to be returned */
X
X /* Make sure we're in range: */
X addr &= 0xffff;
X
X /* If we're just in ROM or RAM, it's easy: */
X if (addr < 0xc000)
X return (MMemory [addr]);
X else if (addr >= 0xd000 && !RamRead) /* rom read enabled */
X return (Rom[addr - 0xd000]);
X else if (addr >= 0xe000) /* 8K RAM read */
X return (MRam[addr - 0xe000]);
X else if (addr >= 0xd000 && addr < 0xe000) /* 4K Bank RAM */
X return (Bank2Enable ? MRam2[addr - 0xd000] : MRam1[addr - 0xd000]);
X
X
X /* We must be in C000 space. Default to random value: */
X data = MegaRand & 0xff;
X MegaRand = (MegaRand + addr * 25 + data) & 0xff;
X
X /* Now do the appropriate memory-mapped INPUT functions, if any: */
X if ((addr >= 0xc000) && (addr <= 0xc00f))
X data = GetC000 ();
X else if ((addr >= 0xc010) && (addr <= 0xc01f))
X data = GetC010 ();
X else if ((addr >= 0xc030) && (addr <= 0xc03f))
X data = GetC030 ();
X else if ((addr >= 0xc080) && (addr <= 0xc08f))
X GetC080 (addr);
X else if (addr == 0xc011)
X data = 0xFF * Bank2Enable;
X else if (addr == 0xc012)
X data = 0xFF * RamRead;
X else if (addr == 0xc701)
X data = 0x20;
X else if (addr == 0xc703)
X data = 0x00;
X else if (addr == 0xc705)
X data = 0x03;
X else if (addr == 0xc7ff)
X data = 0x80;
X else if (addr == 0xc7fe)
X data = 0x1f;
X else if (addr == 0xc7fc)
X data = 0x00;
X else if (addr == 0xc7fd)
X data = 0x00;
X else if (addr == 0xc780)
X data = 0x60;
X
X /* Return the data we came up with to the user: */
X return (BYTE) (data & 0xff);
X}
X
X
X
X
X
X/**************************************************************************/
X/* Memory-Mapped I/O routines: These routines actually perform the spe- */
X/* cific UNIX operations for get/put to certain addresses. Not all Get */
X/* or Put routines must necessarily have a companion Put or Get routine: */
X/* */
X/* * GetXXXXXX: Return a byte of data from an I/O address */
X/* * PutXXXXXX: Receive a byte of data for an I/O address */
X/* */
X/**************************************************************************/
X
X
X
X
X
X/* Get keyboard data in low 7 bits. Msb indicates if hit since last C010: */
XBYTE GetC000 ()
X{
X register int fflags;
X char data = '*';
X
X /* Set nonblocking input: */
X fflags = fcntl (0, F_GETFL, 0);
X (void)fcntl (0, F_SETFL, fflags | O_NDELAY);
X
X /* See if a key was pressed. If yes, update and set hi bit; */
X /* else, leave keyboard latch with last value: */
X if (read (0, &data, 1) > 0)
X MegaLastKey = (int) data | 0x80;
X
X /* Reset nonblocking input: */
X (void)fcntl (0, F_SETFL, fflags);
X
X /* See if user wants to quit: */
X MegaQuitDetect = (data == MEGAQUITKEY);
X
X return (BYTE)MegaLastKey;
X}
X
X
X
X
X
X/* Clear keyboard strobe for C000 msb: */
XBYTE GetC010 ()
X{
X MegaLastKey &= 0x7f; /* Clear strobe bit */
X return (BYTE)(MegaRand & 0xff);
X}
X
Xvoid GetC080 (addr)
XADDR addr;
X{
X switch (addr & 0x000F)
X {
X case 0x00:
X RamRead = 1;
X RamWrite = 0;
X Bank2Enable = 1;
X break;
X case 0x01:
X RamRead = 0;
X RamWrite = 1;
X Bank2Enable = 1;
X break;
X case 0x02:
X RamRead = 0;
X RamWrite = 0;
X Bank2Enable = 1;
X break;
X case 0x03:
X RamRead = 1;
X RamWrite = 1;
X Bank2Enable = 1;
X break;
X case 0x08:
X RamRead = 1;
X RamWrite = 0;
X Bank2Enable = 0;
X break;
X case 0x09:
X RamRead = 0;
X RamWrite = 1;
X Bank2Enable = 0;
X break;
X case 0x0a:
X RamRead = 0;
X RamWrite = 0;
X Bank2Enable = 0;
X break;
X case 0x0b:
X RamRead = 1;
X RamWrite = 1;
X Bank2Enable = 0;
X break;
X }
X}
X
X/*ARGSUSED*/
Xvoid PutC010 (data)
XBYTE data;
X{
X (void)GetC010 (); /* Same thing; either one works */
X}
X
X
X
X/* Beeps speaker if accessed a lot: */
XBYTE GetC030 ()
X{
X static int count = 0;
X
X if (count++ >= 100)
X {
X count = 0;
X /* tputs (TcapBeep,1,MegaPutChar); */
X /* putchar (7); */
X /* fflush (stdout); */
X }
X return (BYTE)(MegaRand & 0xff);
X}
X
X
X/* Handles stores to text/lowres page 1 memory */
Xvoid Put0400 (addr,data)
Xregister ADDR addr;
X BYTE data;
X{
X register int linenum; /* Searching for Y-coordinate */
X register int columnnum; /* Searching for X-coordinate */
X int screenhole = 1; /* Are we in screen hole? */
X
X /* Find which line the given address is in: */
X for (linenum = 0; linenum <= 23; linenum++)
X if ( (addr >= LBasCalc [linenum]) &&
X (addr <= (LBasCalc [linenum] + 39)) )
X {
X columnnum = addr - LBasCalc [linenum];
X screenhole = 0;
X break;
X }
X
X /* If not on screen, don't draw anything: */
X if (screenhole)
X return;
X
X /* Put the terminal cursor at the right location on the screen: */
X /* We assume 80x24 terminal; no need to avoid drawing on last char. */
X move (linenum, columnnum);
X
X /*
X Set terminal into appropriate output mode and do it:
X tputs (TcapInverse,1,MegaPutChar);
X */
X if (data < 64)
X {
X /* attron (A_STANDOUT); */
X standout();
X if (data < 32)
X data += 64;
X }
X else if ((data < 128) && (data > 63))
X {
X standout();
X if (data > 95)
X data -= 64;
X }
X else
X data = data & 127;
X if (!iscntrl ((char) data))
X MegaPutChar ((char) data);
X else
X MegaPutChar ((char) (data - 64));
X standend();
X}
X
X#define _setN_(b) if ((b)!=0) P |= 128; else P &= 0x7f
X#define _setV_(b) if ((b)!=0) P |= 64; else P &= 0xbf
X /* This bit not implemented */
X#define _setB_(b) if ((b)!=0) P |= 16; else P &= 0xef
X#define _setD_(b) if ((b)!=0) P |= 8; else P &= 0xf7
X#define _setI_(b) if ((b)!=0) P |= 4; else P &= 0xfb
X#define _setZ_(b) if ((b)!=0) P |= 2; else P &= 0xfd
X#define _setC_(b) if ((b)!=0) P |= 1; else P &= 0xfe
X
Xstatic unsigned char buffer[512];
XFILE *disk1, /* slot 7, drive 1 280 block 5.25 floppy */
X *disk2; /* slot 7, drive 2 1024 blocks "hard disk" */
X
X#define NBLOCKSD1 280
X#define NBLOCKSD2 1024
X
Xvoid ProInit ()
X{
X MegaPutMem (0x0043, (BYTE)0x70); /* boot disk */
X disk1 = fopen ("PRODOS.IMAGE.D1", "r+");
X disk2 = fopen ("PRODOS.IMAGE.D2", "r+");
X}
X
Xvoid ProFormat (drive)
Xint drive;
X{
X int numblocks = drive ? NBLOCKSD2 : NBLOCKSD1;
X FILE *disk = drive ? disk2 : disk1;
X
X (void)fseek (disk, (long)(512 * numblocks), 0);
X (void)fwrite (" ", 1, 1, disk);
X}
X
Xvoid ProRead (drive)
Xint drive;
X{
X register int i;
X int block = MegaGetMem (0x46) + MegaGetMem (0x47) * 0x0100;
X int buf = MegaGetMem (0x44) + MegaGetMem (0x45) * 0x0100;
X FILE *disk = drive ? disk2 : disk1;
X
X (void)fseek (disk, (long)(block * 512), 0);
X
X (void)fread ((char *)buffer, 1, 512, disk);
X for (i = 0; i < 512; i++)
X MegaPutMem (buf + i, (BYTE)buffer[i]);
X _setC_(0); /* CLC */
X A = 0;
X}
X
Xvoid ProWrite (drive)
Xint drive;
X{
X register int i;
X int block = MegaGetMem (0x46) + MegaGetMem (0x47) * 0x0100;
X int buf = MegaGetMem (0x44) + MegaGetMem (0x45) * 0x0100;
X FILE *disk = drive ? disk2 : disk1;
X
X (void)fseek (disk, (long)(block * 512), 0);
X
X for (i = 0; i < 512; i++)
X buffer[i] = MegaGetMem (buf + i);
X
X (void)fwrite ((char *)buffer, 1, 512, disk);
X _setC_(0); /* CLC */
X A = 0;
X}
X
Xvoid ProStatus (drive)
Xint drive;
X{
X int numblocks = drive ? NBLOCKSD2 : NBLOCKSD1;
X
X _setC_(0); /* CLC */
X A = 0; /* LDA #0 */
X Y = numblocks / 256;
X X = numblocks % 256;
X}
X
Xvoid prodos()
X{
X int drive = ((MegaGetMem (0x43) >= 128) ? 1 : 0);
X
X switch (MegaGetMem (0x42))
X {
X case 0:
X ProStatus (drive); break;
X case 1:
X ProRead (drive); break;
X case 2:
X ProWrite (drive); break;
X case 3:
X ProFormat (drive); break;
X }
X}
X
END_OF_FILE
echo shar: NEWLINE appended to \"'mega2.c'\"
if test 12595 -ne `wc -c <'mega2.c'`; then
echo shar: \"'mega2.c'\" unpacked with wrong size!
fi
# end of 'mega2.c'
fi
echo shar: End of archive 2 \(of 2\).
cp /dev/null ark2isdone
MISSING=""
for I in 1 2 ; do
if test ! -f ark${I}isdone ; then
MISSING="${MISSING} ${I}"
fi
done
if test "${MISSING}" = "" ; then
echo You have unpacked both archives.
rm -f ark[1-9]isdone
else
echo You still need to unpack the following archives:
echo " " ${MISSING}
fi
## End of shell archive.
exit 0