[comp.sys.cbm] Terminal program source in C

mat@emcard.UUCP (W Mat Waites) (04/04/89)

#!/bin/sh
# shar:	Shell Archiver  (v1.22)
#
#	Run the following text with /bin/sh to create:
#	  ReadMe
#	  asctocbm.c
#	  cbmtoasc.c
#	  cmast.c
#	  dial.c
#	  dos.c
#	  menus.c
#	  screen.c
#	  xmodem.c
#
sed 's/^X//' << 'SHAR_EOF' > ReadMe &&
XThis is the first UseNet distribution of the Cmaster terminal program.
X
XThese are the files in the distribution:
X
Xasctocbm.c
Xcbmtoasc.c
Xcmast.c
Xdial.c
Xdos.c
Xmenus.c
Xscreen.c
Xxmodem.c
X
X
Xasctocbm.c and cbmtoasc.c are unix utilities to convert to Commodore ascii
Xand vice versa.
X
XThe rest are Power C source code for the terminal.
X
Xcmast.c - main program
Xdial.c - hayes dialer
Xdos.c - dos support, including serial port routines
Xmenus.c - menu for setting parameters
Xscreen.c - screen access routines, color, cursor positioning, etc.
Xxmodem.c - xmodem protocol
X
XWhen linking, you will have to link libraries after linking 3 or so files,
Xand then continue with the linking process. The linker cannot hold all of the
Xreferences in one pass.
X
XHave fun and send me comments.
X
XW Mat Waites
Xgatech!emcard!mat
X8-5 ET: (404) 727-7197
SHAR_EOF
chmod 0664 ReadMe || echo "restore of ReadMe fails"
sed 's/^X//' << 'SHAR_EOF' > asctocbm.c &&
X/* asctocbm.c - convert real ascii to cbm ascii */
X
X#include <stdio.h>
X#include <strings.h>
X#include <ctype.h>
X
Xmain(argc, argv)
Xint argc;
Xchar *argv[];
X{
X	FILE *infile, *outfile;
X	char *strchr();
X	int i;
X	char nambuf[30];
X	char stripped[30];
X
X	if(argc > 1)
X		{
X		for(i=1; i<argc; i++)
X			{
X			strcpy(stripped, argv[i]);
X			if(strchr(stripped, '.'))
X				{
X				*strchr(stripped, '.') = '\0';
X				}
X			fprintf(stderr, "Translating %s\n", stripped);
X
X			strcpy(nambuf, stripped);
X			strcat(nambuf, ".c");
X			if(infile = fopen(nambuf, "r"))
X				{
X				strcpy(nambuf, stripped);
X				strcat(nambuf, ".cbm");
X				if(outfile = fopen(nambuf, "w"))
X					{
X					convert(infile, outfile);
X					fclose(outfile);
X					}
X				else
X					{
X					fprintf(stderr, "Couldn't open %s\n", nambuf);
X					}
X				fclose(infile);
X				}
X			else
X				{
X				fprintf(stderr, "Couldn't open %s\n", nambuf);
X				}
X			}
X		}
X	else
X		{
X		convert(stdin, stdout);
X		}
X
X}
X
Xconvert(infile, outfile)
XFILE *infile, *outfile;
X{
X	int c, a;
X
X	while((c=getc(infile)) != EOF)
X		{
X		if(c == '\n')
X			{
X			a = '\r';
X			}
X		else if(c >= 'a' && c <= 'z')
X			{
X			a = c - 'a' + 'A';
X			}
X		else if(c >= 'A' && c <= 'Z')
X			{
X			a = c - 'A' + 193;
X			}
X		else if(c == '~')
X			{
X			a = 175;
X			}
X		else if(c == '{')
X			{
X			a = 219;
X			}
X		else if(c == '}')
X			{
X			a = 221;
X			}
X		else if(c == '|')
X			{
X			a = 223;
X			}
X		else if(isprint(c))
X			{
X			a = c;
X			}
X		else if(c == 0)
X			{
X			continue;
X			}
X		else
X			{
X			fprintf(outfile, "{{%d}}", c);
X			continue;
X			}
X		putc(a, outfile);
X		}
X}
X
SHAR_EOF
chmod 0660 asctocbm.c || echo "restore of asctocbm.c fails"
sed 's/^X//' << 'SHAR_EOF' > cbmtoasc.c &&
X/* cbmtoasc.c - convert cbm ascii to real ascii */
X
X#include <stdio.h>
X#include <strings.h>
X#include <ctype.h>
X
Xmain(argc, argv)
Xint argc;
Xchar *argv[];
X{
X	FILE *infile, *outfile;
X	char *strchr();
X	int i;
X	char nambuf[30];
X	char stripped[30];
X
X	if(argc > 1)
X		{
X		for(i=1; i<argc; i++)
X			{
X			strcpy(stripped, argv[i]);
X			if(strchr(stripped, '.'))
X				{
X				*strchr(stripped, '.') = '\0';
X				}
X			fprintf(stderr, "Translating %s\n", stripped);
X
X			strcpy(nambuf, stripped);
X			strcat(nambuf, ".cbm");
X			if(infile = fopen(nambuf, "r"))
X				{
X				strcpy(nambuf, stripped);
X				strcat(nambuf, ".c");
X				if(outfile = fopen(nambuf, "w"))
X					{
X					convert(infile, outfile);
X					fclose(outfile);
X					}
X				else
X					{
X					fprintf(stderr, "Couldn't open %s\n", nambuf);
X					}
X				fclose(infile);
X				}
X			else
X				{
X				fprintf(stderr, "Couldn't open %s\n", nambuf);
X				}
X			}
X		}
X	else
X		{
X		convert(stdin, stdout);
X		}
X
X}
X
X
Xconvert(infile, outfile)
XFILE *infile, *outfile;
X{
X	int c, a;
X
X	while((c=getc(infile)) != EOF)
X		{
X		if(c == '\r')
X			{
X			a = '\n';
X			}
X		else if(c >= 'A' && c <= 'Z')
X			{
X			a = c - 'A' + 'a';
X			}
X		else if(c >= 193 && c <= 218)
X			{
X			a = c - 193 + 'A';
X			}
X		else if(c == 175)
X			{
X			a = '~';
X			}
X		else if(c == 219)
X			{
X			a = '{';
X			}
X		else if(c == 221)
X			{
X			a = '}';
X			}
X		else if(c == 223)
X			{
X			a = '|';
X			}
X		else if(isprint(c))
X			{
X			a = c;
X			}
X		else if(c == 0)
X			{
X			continue;
X			}
X		else
X			{
X			/* fprintf(outfile, "{{%d}}", c); */
X			continue;
X			}
X		putc(a, outfile);
X		}
X}
X
SHAR_EOF
chmod 0660 cbmtoasc.c || echo "restore of cbmtoasc.c fails"
sed 's/^X//' << 'SHAR_EOF' > cmast.c &&
X/* cmast.c - main for cmaster */
X/* W Mat Waites - Sept 1988 */
X
X#include <stdio.h>
X
X#define DEFBAUD  1200
X#define DEFDRIVE 8
X#define DEFMODE  1
X#define DEFLF    0
X#define DEFFLOW  0
X
X/* globals */
Xint echo;
Xint baud;
Xint bpchar;
Xint stopbits;
Xint parity;
Xint ddrive;
Xint done;
Xint asciimode;
Xint autolf;
Xint flowcont;
X
X/* main() - main program for cmast */
Xmain(argc, argv)
Xunsigned argc;
Xchar *argv[];
X{
X  int kbchar, serchar;
X  int outch;
X  char *qtsw = 0x00d4;
X
X  /* initialization */
X  done = 0;
X  echo = 0;
X  baud = DEFBAUD;
X  bpchar = 8;
X  stopbits = 1;
X  parity = 0;  /* 0 - none */
X  ddrive = DEFDRIVE;
X  asciimode = DEFMODE;
X  autolf = DEFLF;
X  flowcont = DEFFLOW;
X
X  /* check for baud rate parameter */
X  if(argc > 1)
X    {
X    baud = atoi(argv[1]);
X    switch(baud)
X      {
X      case 300:
X      case 450:
X      case 1200:
X        break;
X      default:
X        baud = DEFBAUD;
X        break;
X      }
X    }
X
X  /* open files */
X  openkb();
X  openserial();
X  setserial(baud, bpchar,
X    stopbits, parity);
X
X  /* set colors */
X  setborder(15);
X  setbg(0);
X  setfg(1);
X
X  initscr();
X  titlescr();
X  sleep(2);
X
X  erase();
X  move(0, 0);
X  cursoron();
X  updatecursor();
X
X  /* main loop */
X  while(!done)
X    {
X
X    /* check for <C=> key */
X    if(chkstop())
X      {
X      cursoroff();
X      menu();
X      updatecursor();
X      cursoron();
X      }
X
X    /* check keyboard */
X    while(charsinq() > 0 && !done)
X      {
X      kbchar = getkb();
X
X      if(kbchar >= 133 &&
X         kbchar <= 140)
X        {
X        /* function keys */
X        putfunct(kbchar);
X        }
X      else
X        {
X        /* non-function keys */
X        if(echo)  /* local echo */
X          {
X          if(isprint(kbchar) ||
X            iscntrl(kbchar))
X            {
X            putchar(kbchar);
X            updatecursor();
X            }
X          }
X        outch = cbmtoasc(kbchar);
X        if(outch)
X          {
X          putserial(outch);
X          }
X        }
X      }
X
X    /* check serial port */
X    if(!done)
X      {
X      while((serchar=getserial()) >= 0)
X        {
X        if(asciimode)
X          {
X          /* mask to 7 bits */
X          serchar &= 0x7f;
X          switch(serchar)
X            {
X            case 7:   /* BEL */
X              break;
X            case 8:   /* BS */
X              putchar(157);
X              break;
X            case 9:   /* TAB */
X              putchar(' ');
X              putchar(' ');
X              break;
X            case 10:  /* LF */
X              putchar(17);
X              break;
X            case 12:  /* FF */
X              erase();
X              break;
X            case 13:  /* CR */
X              putchar(13);
X              if(!autolf)
X                {
X                putchar(145);  /* move up */
X                }
X              break;
X            default:
X              outch=asctocbm(serchar);
X              if(outch)
X                {
X                putchar(outch);
X                }
X              else  /* unexpected char */
X                {
X                /*printf("[%d]", serchar);*/
X                /*putchar(serchar);*/
X                }
X              break;
X            }
X          }
X        else
X          {
X          putchar(serchar);
X          }
X        *qtsw = 0;
X        updatecursor();
X        if(charsinq() > 0 || chkstop())
X          {
X          break;
X          }
X        }
X      }
X    }
X
X  /* shut down */
X  cursoroff();
X  erase();
X  closekb();
X  closeserial();
X  move(23, 0);
X}
X
X/* cmaster support routines */
X
X/* titlescr() - initial display */
Xtitlescr()
X{
X  erase();
X  center(5, "Cmaster Terminal");
X  center(15, "Press <C=> for menu");
X}
X
X/* cbmtoasc() - convert cbm to ascii */
Xcbmtoasc(cbmchar)
Xint cbmchar;
X{
X  if(!asciimode)
X    {
X    return(cbmchar);
X    }
X
X  /* lower case */
X  if(cbmchar >= 65 && cbmchar <= 90)
X    {
X    return(cbmchar + 32);
X    }
X
X  /* upper case */
X  if(cbmchar >= 193 && cbmchar <= 218)
X    {
X    return(cbmchar - 128);
X    }
X
X  if(cbmchar == 20)  /* backspace */
X    {
X    return(8);
X    }
X
X  return(cbmchar);
X}
X
X/* asctocbm() - convert ascii to cbm */
Xasctocbm(ascchar)
Xint ascchar;
X{
X  if(!asciimode)
X    {
X    return(ascchar);
X    }
X
X  /* upper case */
X  if(ascchar >= 65 && ascchar <= 90)
X    {
X    return(ascchar + 128);
X    }
X
X  /* lower case */
X  if(ascchar >= 97 && ascchar <= 122)
X    {
X    return(ascchar - 32);
X    }
X
X  if(ascchar >= 32 && ascchar <= 64)
X    {
X    return(ascchar);
X    }
X
X  if(ascchar >= 91 && ascchar <= 95)
X    {
X    return(ascchar);
X    }
X
X  return 0;
X}
X
X/* putserstr() - put string to port */
Xputserstr(str)
Xchar *str;
X{
X  while(*str)
X    {
X    putserial(cbmtoasc((int)*str));
X    str++;
X    }
X}
X
Xstatic char *funcstr[8] =
X  {
X  "Function Key 1\n",
X  "Function Key 3\n",
X  "Function Key 5\n",
X  "Function Key 7\n",
X  "Function Key 2\n",
X  "Function Key 4\n",
X  "Function Key 6\n",
X  "Function Key 8\n"
X  };
X
X/* putfunct() - put function key string */
Xputfunct(key)
Xint key;
X{
X  unsigned lockey;
X
X  if(key < 133 || key > 140)
X    {
X    return;
X    }
X
X  lockey = key - 133;
X
X  putserstr(funcstr[lockey]);
X
X}
X/* end of file */
SHAR_EOF
chmod 0664 cmast.c || echo "restore of cmast.c fails"
sed 's/^X//' << 'SHAR_EOF' > dial.c &&
X/* dial.c - dial routines for cmaster */
X/* W Mat Waites - Sept 1988 */
X
X#include <stdio.h>
X#include <strings.h>
X
X#define DIALTIME 20
X#define WAITTIME  5
X#define NUMTRIES  5
X
Xchar *prefix    = "atdt";
Xchar *suffix    = "\r";
Xchar *attnstr   = "+++";
Xchar *hangupstr = "ath\r";
Xchar *initstr   = "ats7=45e0q0v1x4\r";
X
Xchar *moddial();
X
Xstatic int tmo;
X
X/* dial() - main dialing menu */
Xdial()
X{
X  int len, i, nt, wt, dt;
X  char numbuf[21];
X  char *msg;
X
X  nt = NUMTRIES;
X  wt = WAITTIME;
X  dt = DIALTIME;
X
X  center(1, "Cmaster Dialing Menu");
X
X  putmodstr(initstr);
X  sleep(1);
X  while(getserial() >= 0)
X    {
X    }
X
X  move(5, 5);
X  printf("Number: ");
X  len = getstring(numbuf, 20);
X  if(len <= 0)
X    {
X    return(0);
X    }
X
X  move(8, 5);
X  printf("Trying %2d times:\n", nt);
X
X  for(i=1; i<=nt; i++)
X    {
X    move(10, 5);
X    printf("Dialing %s try %2d\n",
X      numbuf, i);
X    while(getserial() >= 0)
X      {
X      }
X    msg = moddial(numbuf, dt);
X
X    if(atoi(msg) > 0)
X      {
X      move(12, 5);
X      printf("Connected!\n");
X      sleep(1);
X      return(1);
X      }
X    else
X      {
X      move(14, 5);
X      printf("Dial failed - %s\n", msg);
X      if(strcmp(msg, "ABORT") == 0)
X        {
X        sleep(3);
X        return(0);
X        }
X      if(i <= nt)  /* tries left? */
X        {
X        sleep(wt);
X        }
X      }
X    }
X  return(0);
X}
X
X/* moddial() - main dialing menu */
Xstatic char *moddial(numstr, dtime)
Xchar *numstr;
Xint dtime;
X{
X  int i, skip, timer, modch, rc;
X  char s[50], number[40], rcbuf[60];
X  char *n;
X
X  s[0] = '\0';
X  strcpy(s, prefix);
X
X  /* clean up number */
X  i = 0;
X  skip = 0;
X  n = numstr;
X  while (*n)
X    {
X    if(*n == '\\' && !skip)
X      {
X      skip++;
X      n++;
X      continue;
X      }
X    if(!index("-() ", *n) || skip)
X      number[i++] = *n;
X    n++;
X    skip = 0;
X    }
X  number[i] = '\0';
X
X  strcat(s, number);
X  strcat(s, suffix);
X
X  putmodstr(s);
X
X  rc=0;
X  timer=0;
X  rcbuf[0] = '\0';
X
X  while(rc < 59 && timer < dtime)
X    {
X    modch = getmodch(1);
X    if(!tmo)
X      {
X      /* ignore LF */
X      if(modch == 0x0a)
X        {
X        continue;
X        }
X
X      /* ignore leading CR */
X      if(modch == 0x0d)
X        {
X        if(rc == 0)
X          {
X          continue;
X          }
X        modch = '!';
X        }
X
X      rcbuf[rc] = modch;
X      rcbuf[rc+1] = '\0';
X      rc++;
X      }
X    else
X      {
X      timer++;
X      }
X
X    /* printf("answer is '%s'\n", rcbuf); */
X
X    if(strcmp(rcbuf, "connect!") == 0)
X      return("300");
X
X    if(strcmp(rcbuf, "connect 1200!") == 0)
X      return("1200");
X
X    if(strcmp(rcbuf, "busy!") == 0)
X      return("BUSY");
X
X    if(strcmp(rcbuf, "voice!") == 0)
X      return("VOICE");
X
X    if(strcmp(rcbuf, "no carrier!") == 0)
X      return("NO CARRIER");
X
X    if(chkstop())
X      {
X      /* aborted */
X      putmodstr("\r");
X      sleep(1);
X      while(getserial() >= 0)
X        {
X        }
X      return("ABORT");
X      }
X
X    }
X
X  /* didn't connect */
X  putmodstr("\r");
X  sleep(1);
X  while(getserial() >= 0)
X    {
X    }
X  return("ERROR");
X}
X
X/* modhangup() - hangup modem */
Xmodhangup()
X{
X  char *match;
X
X  sleep(2);
X  putmodstr(attnstr);
X  sleep(2);
X  putmodstr(hangupstr);
X  sleep(1);
X  while(getserial() >= 0)
X    {
X    }
X}
X
X/* putmodstr() - put string to modem */
Xstatic putmodstr(str)
Xchar *str;
X{
X  while(*str)
X    {
X    putserial(*str);
X    str++;
X    }
X}
X
X/* getmodch() - get char w/timeout */
Xstatic getmodch(timlen)
Xint timlen;
X{
X  int serchar;
X
X  tmo = 0;
X  setclock((unsigned)0); /* start timer */
X
X  for(;;)
X    {
X
X    serchar = getserial();
X    if(serchar >= 0)
X      {
X      return(serchar);
X      }
X
X    if(getclock() >= timlen)
X      {
X      tmo = 1;
X      return 0;
X      }
X    }
X}
X
X/* end of file */
SHAR_EOF
chmod 0664 dial.c || echo "restore of dial.c fails"
sed 's/^X//' << 'SHAR_EOF' > dos.c &&
X/* dos2.c - operating system stuff:
X  disk support
X  serial i/o
X  kb i/o
X  timers */
X/* W Mat Waites - Sept 1988 */
X
X#include <stdio.h>
X
X/* 5-9 may be used with "basic" open */
X#define KB 5
X#define RS 6
X
X/* kludge for reliable 1200 baud */
X#define KLUDGE 40
X
X/* flow control params */
X#define FLOWON  30
X#define FLOWOFF 128
X#define XON     17
X#define XOFF    19
X
X/* kernel routines */
X#define CHKIN  0xffc6
X#define GETIN  0xffe4
X#define TKSA   0xff96
X#define ACPTR  0xffa5
X#define TALK   0xffb4
X#define UNTLK  0xffab
X
X/* input and output serial buffers */
Xstatic char inbuf[256], otbuf[256];
X
X/* flow control turned on? */
Xchar flowact = 1;
X
X/* serial interface functions */
X
X/* openserial() - open serial port */
Xopenserial()
X{
X  short *ribuf = 0x00f7;
X  short *robuf = 0x00f9;
X
X  /* open serial port */
X  open(RS, 2, 0, "");
X
X  /* move pointers to buffers */
X  *ribuf = inbuf;
X  *robuf = otbuf;
X}
X
X/* closeserial() - close serial port */
Xcloseserial()
X{
X  close(RS);
X}
X
X/* 300, 450, 1200 implemented */
X
Xstatic short hibyte[3] = { 6,  4,  1};
Xstatic short lobyte[3] = {68, 12, 61};
X
X/* setserial() - set serial port */
Xsetserial(bd, bpc, sb, par)
Xint bd, bpc, sb, par;
X{
X  short *m51ajb = 0x0295;
X  short *baudof = 0x0299;
X  char  *m51ctr = 0x0293;
X  char  *m51cdr = 0x0294;
X  char  *bitnum = 0x0298;
X  unsigned indx;
X
X  switch(bd)
X    {
X    case 300:
X      indx = 0;
X      break;
X    case 450:
X      indx = 1;
X      break;
X    case 1200:
X      indx = 2;
X      break;
X    default:  /* default to 300 baud */
X      indx = 0;
X      break;
X    }
X
X  /* set baud rate */
X  *m51ajb = 256 * hibyte[indx] +
X    lobyte[indx];
X  *baudof = (*m51ajb)*2 + 200;
X
X  /* stopbits */
X  if(sb < 1 || sb > 2)
X    {
X    sb = 1;
X    }
X  sb--;
X
X  /* bits per char */
X  if(bpc < 5 || bpc > 8)
X    {
X    bpc = 8;
X    }
X  *bitnum = (char)(bpc + 1);
X  bpc = 8 - bpc;
X
X  /* parity */
X  if(par < 0 || par > 7)
X    {
X    par = 0;
X    }
X
X  /* put bpc, sb, and par in regs */
X  *m51ctr = (char)((bpc << 5)
X    | (sb << 7));
X  *m51cdr = (char)(par << 5);
X}
X
X/* setflow() - set flowcontrol */
Xsetflow(mode)
Xint mode;
X{
X  flowact = mode;
X}
X
X
X/* getserial() - char from serial port */
Xgetserial()
X{
X  int ch;
X  char diff;
X  static char *rsstat = 0x0297;
X  static char *ridbe  = 0x029b;
X  static char *ridbs  = 0x029c;
X  static char flowing = 1;
X
X  ch = getonechar(RS);
X
X  /* check for empty buffer */
X  if((*rsstat & 0x08) == 0x08)
X    {
X    return -1;
X    }
X  else
X    {
X    if(flowact)
X      {
X      /* check for flow control */
X      if(*ridbs > *ridbe)
X        {
X        diff = *ridbs - *ridbe;
X        }
X      else
X        {
X        diff = *ridbe - *ridbs;
X        }
X
X      if(flowing)
X        {
X        if(diff > FLOWOFF)
X          {
X          putserial(XOFF);
X          flowing = 0;
X          }
X        }
X      else
X        {
X        if(diff < FLOWON)
X          {
X          putserial(XON);
X          flowing = 1;
X          }
X        }
X      }
X    return ch;
X    }
X}
X
X/* putserial() - char to serial port */
Xputserial(ch)
Xchar ch;
X{
X  int i;
X
X  putc(ch, RS);
X
X  /* delay loop for 1200 baud kludge */
X  for(i=0; i<KLUDGE; i++)
X    {
X    }
X}
X
X
X/* keyboard interface functions */
X
X/* openkb() - open keyboard */
Xopenkb()
X{
X  char *rptflg = 0x028a;
X
X  open(KB, 0, 0, "");
X  /* let the keyboard repeat */
X  *rptflg = 0x80; 
X}
X
X/* closekb() - close keyboard */
Xclosekb()
X{
X  close(KB);
X}
X
X/* getkb() - get char from keyboard */
Xgetkb()
X{
X  return getonechar(KB);
X}
X
X/* charsinq() - # available kb chars */
Xcharsinq()
X{
X  char *ndx = 0x00c6;
X
X  return (int)*ndx;
X}
X
X/* chkstop() - check for <C=> key */
Xchkstop()
X{
X  char *shflag = 0x028d;
X
X  return(*shflag == 0x02);
X}
X
X/* getonechar() - get char from chan */
Xstatic getonechar(channel)
Xint channel;
X{
X  char ac, xc, yc;
X
X  xc = (char)channel;
X  sys(CHKIN, &ac, &xc, &yc);
X  sys(GETIN, &ac, &xc, &yc);
X  return(int)ac;
X}
X
X
X/* disk i/o functions */
X
X#define SADDR  0x6f
X
X/* diskerr() - read error channel */
Xchar *diskerr(disk)
Xint disk;
X{
X  int cc;
X  char ac, xc, yc;
X  static char msgbuf[41];
X  char *mp;
X  char *second = 0x00b9;
X  char *status = 0x0090;
X
X  /* tell drive to talk */
X  ac = (char)disk;
X  sys(TALK, &ac, &xc, &yc);
X
X  /* tell it what to talk about */
X  ac = (char)SADDR;
X  *second = SADDR;
X  sys(TKSA, &ac, &xc, &yc);
X
X  /* read in the response */
X  mp = msgbuf;
X  for(;;)
X    {
X    /* get byte from bus in acc */
X    sys(ACPTR, &ac, &xc, &yc);
X 
X    if(ac == '\r')
X      {
X      break;
X      }
X    *mp = ac;
X    mp++;
X    }
X  *mp = '\0';
X
X  /* tell drive to shut up */
X  sys(UNTLK, &ac, &xc, &yc);
X
X  return(msgbuf);
X}
X
X
X/* timer functions */
X
Xunsigned getclock();
X
X/* sleep() - sleep for seconds */
Xsleep(usecs)
Xunsigned usecs;
X{
X  setclock((unsigned)0);
X
X  while(getclock() < usecs)
X    {
X    }
X}
X
Xstruct clock  /* struct matches CIA */
X  {
X  char tenths;
X  char seconds;
X  char minutes;
X  char hours;
X  };
X
X/* setclock() - set timer clock */
Xsetclock(usecs)
Xunsigned usecs;
X{
X  unsigned bsecs;
X  struct clock *clock1 = 0xdc08;
X  char *c1mode = 0xdc0f;
X
X  *c1mode &= 0x7f;  /* mode is clock */
X
X  if(usecs > 59) usecs = 59;
X
X  /* convert secs to bcd */
X  bsecs = usecs%10 | ((usecs/10)<<4);
X
X  clock1->hours = 0;
X  clock1->minutes = 0;
X  clock1->seconds = (char)bsecs;
X  clock1->tenths = 0;  /* free clock */
X}
X
X/* getclock() - get current clock secs */
Xunsigned getclock()
X{
X  unsigned usecs;
X  char junk;
X  struct clock *clock1 = 0xdc08;
X
X  junk = clock1->seconds;
X  usecs = (junk & 0x0f) +
X          10 * (junk >> 4);
X
X  junk = clock1->tenths; /* free clock */
X
X  return usecs;
X}
X
X/* end of file */
SHAR_EOF
chmod 0664 dos.c || echo "restore of dos.c fails"
sed 's/^X//' << 'SHAR_EOF' > menus.c &&
X/* menus.c - menu routines */
X/* W Mat Waites - Sept 1988 */
X
X#include <stdio.h>
X
X/* globals */
Xextern int done;
Xextern int echo;
Xextern int baud;
Xextern int bpchar;
Xextern int stopbits;
Xextern int parity;
Xextern int ddrive;
Xextern int asciimode;
Xextern int autolf;
Xextern int flowcont;
X
Xchar *parval();
Xchar *dupval();
Xchar *modeval();
X
X/* menu() - main menu */
Xmenu()
X{
X  int mdone = 0;
X  int kbchar;
X  int ycoord;
X  char fname[21];
X
X  /* draw menu */
X  erase();
X  while(!mdone)
X    {
X    center(1, "Cmaster Main Menu");
X
X    ycoord = 4;
X    move(ycoord++, 5);
X    printf("(Q)uit");
X    ycoord++;
X
X    move(ycoord++, 5);
X    printf("(A)utodial");
X    move(ycoord++, 5);
X    printf("(H)angup");
X    move(ycoord++, 5);
X    printf("(U)pload");
X    move(ycoord++, 5);
X    printf("(D)ownload");
X    move(ycoord++, 5);
X    printf("(N)umber of drive        %1d",
X      ddrive);
X    ycoord++;
X
X    move(ycoord++, 5);
X    printf("(M)ode          %10s",
X      modeval(asciimode));
X    move(ycoord++, 5);
X    printf("(L)inefeeds     %10s",
X      lfval(autolf));
X    move(ycoord++, 5);
X    printf("(T)og Duplex    %10s",
X      dupval(echo));
X    move(ycoord++, 5);
X    printf("(F)low Control  %10s",
X      boolval(flowcont));
X    ycoord++;
X
X    move(ycoord++, 5);
X    printf("(B)aud                %4d",
X      baud);
X    move(ycoord++, 5);
X    printf("(C)har length            %1d",
X      bpchar);
X    move(ycoord++, 5);
X    printf("(P)arity        %10s",
X      parval(parity));
X    move(ycoord++, 5);
X    printf("(S)top bits              %1d",
X      stopbits);
X    ycoord++;
X
X    move(ycoord, 5);
X    printf("(R)eturn");
X
X    /* wait for keypress */
X    while(charsinq() == 0)
X      {
X      }
X    kbchar = getkb();
X
X    /* switch on key */
X    switch(kbchar)
X      {
X      case 'q': /* quit */
X      case 'Q':
X        done = 1;
X        mdone = 1;
X        break;
X      case 'a': /* autodial */
X      case 'A':
X        erase();
X        if(dial())
X          {
X          mdone = 1;
X          }
X        erase();
X        break;
X      case 'h': /* hangup */
X      case 'H':
X        modhangup();
X        break;
X      case 'u': /* upload */
X      case 'U':
X        erase();
X        center(1, "Cmaster Upload Menu");
X        move(5, 5);
X        printf("Filename:");
X        if(getstring(fname, 16))
X          {
X          erase();
X          setserial(baud, 8, 1, 0);
X          setflow(0);
X          sendfile(fname, ddrive);
X          setserial(baud, bpchar,
X            stopbits, parity);
X          setflow(flowcont);
X          printf("Press key:");
X          while(charsinq() == 0)
X            {
X            }
X          kbchar = getkb();
X          mdone = 1;
X          }
X        erase();
X        break;
X      case 'd': /* download */
X      case 'D':
X        erase();
X        center(1, "Cmaster Download Menu");
X        move(5, 5);
X        printf("Filename:");
X        if(getstring(fname, 15))
X          {
X          erase();
X          setserial(baud, 8, 1, 0);
X          setflow(0);
X          recvfile(fname, ddrive);
X          setserial(baud, bpchar,
X            stopbits, parity);
X          setflow(flowcont);
X          printf("Press key:");
X          while(charsinq() == 0)
X            {
X            }
X          kbchar = getkb();
X          mdone = 1;
X          }
X        erase();
X        break;
X      case 'n':  /* change active drive */
X      case 'N':
X        if(ddrive == 8)
X          {
X          ddrive = 9;
X          }
X        else
X          {
X          ddrive = 8;
X          }
X        break;
X      case 'b': /* cycle baud */
X      case 'B':
X        switch(baud)
X          {
X          case 300:
X            baud = 450;
X            break;
X          case 450:
X            baud = 1200;
X            break;
X          case 1200:
X            baud = 300;
X            break;
X          default:
X            baud = 300;
X            break;
X          }
X        setserial(baud, bpchar,
X                  stopbits, parity);
X        break;
X      case 'c': /* bits per char */
X      case 'C':
X        bpchar++;
X        if(bpchar > 8)
X          {
X          bpchar = 7;
X          }
X        setserial(baud, bpchar,
X                  stopbits, parity);
X        break;
X      case 'p': /* parity */
X      case 'P':
X        if(parity == 0)
X          {
X          parity = 1;
X          }
X        else
X          {
X          parity += 2;
X          if(parity > 7)
X            {
X            parity = 0;
X            }
X          }
X        setserial(baud, bpchar,
X                  stopbits, parity);
X        break;
X      case 's': /* stopbits (1 or 2) */
X      case 'S':
X        stopbits++;
X        if(stopbits > 2)
X          {
X          stopbits = 1;
X          }
X        setserial(baud, bpchar,
X                  stopbits, parity);
X        break;
X      case 'm': /* mode */
X      case 'M':
X        asciimode = !asciimode;
X        break;
X      case 'l': /* auto LF */
X      case 'L':
X        autolf = !autolf;
X        break;
X      case 'f': /* flow control */
X      case 'F':
X        flowcont = !flowcont;
X        setflow(flowcont);
X        break;
X      case 't': /* toggle local echo */
X      case 'T':
X        echo = !echo;
X        break;
X      case 'r': /* return to terminal */
X      case 'R':
X        mdone = 1;
X        break;
X      default:
X        break;
X      }
X    }
X  erase();
X}
X
X/* parval() - return parity string */
Xstatic char *parval(par)
Xint par;
X{
X  switch(par)
X    {
X    case 0:
X      return("Disabled");
X    case 1:
X      return("Odd");
X    case 3:
X      return("Even");
X    case 5:
X      return("Mark");
X    case 7:
X      return("Space");
X    default:
X      return("Invalid");
X    }
X}
X
X/* dupval() - return duplex string */
Xstatic char *dupval(ech)
Xint ech;
X{
X  if(ech)
X    {
X    return("Half");
X    }
X  else
X    {
X    return("Full");
X    }
X}
X
X/* modeval() - return display mode string */
Xstatic char *modeval(mode)
Xint mode;
X{
X  if(mode)
X    {
X    return("ASCII");
X    }
X  else
X    {
X    return("Graphics");
X    }
X}
X
X/* lfval() - return linefeed mode string */
Xstatic char *lfval(mode)
Xint mode;
X{
X  if(mode)
X    {
X    return("Auto LF");
X    }
X  else
X    {
X    return("No Auto LF");
X    }
X}
X
X/* boolval() - return On/Off string */
Xstatic char *boolval(mode)
Xint mode;
X{
X  if(mode)
X    {
X    return("On");
X    }
X  else
X    {
X    return("Off");
X    }
X}
X
X/* end of file */
SHAR_EOF
chmod 0664 menus.c || echo "restore of menus.c fails"
sed 's/^X//' << 'SHAR_EOF' > screen.c &&
X/* screen.c - screen routines */
X/* W Mat Waites - Sept 1988 */
X
X#include <stdio.h>
X
X#define PLOT 0xfff0
X
X/* initscr() - initialize cursor, etc */
Xinitscr()
X{
X  int i;
X  char *tmp;
X  char *sprite0 = 0x07f8;
X  char *spcol0 =  0xd027;
X
X  /* cursor sprite is defined in
X     basic input buffer */
X
X  /* put sprite 0 at $0200 */
X  *sprite0 = 8;
X
X  /* set color to white */
X  *spcol0 = 1;
X
X  /* first, set sprite to all bgd */
X  for(i=0; i<64; i++)
X    {
X    tmp = 0x0200 + i;
X    *tmp = 0;
X    }
X
X  /* fill in fgd of sprite */
X  for(i=7; i<8; i++)
X    {
X    tmp = 0x0200 + 3*i;
X    *tmp = 0xff;
X    }
X}
X
X/* cursoron() - turn on cursor */
Xcursoron()
X{
X  char *enab = 0xd015;
X
X  *enab = 0x01; /* turn on sprite 0 */
X}
X
X/* cursoroff() - turn off cursor */
Xcursoroff()
X{
X  char *enab = 0xd015;
X
X  *enab = 0x00; /* turn off sprite 0 */
X}
X
X/* updatecursor() - correct sprite loc */
Xupdatecursor()
X{
X  char *cury, *curx;
X  char *ypos, *xpos;
X  char *highx;
X  int sy, sx;
X
X  cury = 0x00d6;
X  curx = 0x00d3;
X  xpos = 0xd000;
X  ypos = 0xd001;
X  highx = 0xd010;
X
X  /* compute cursor screen location */
X  sy = (int)*cury;
X  sx = (int)*curx;
X  if(sx > 39) /* check for wrap */
X    {
X    sx -= 40;
X    }
X  sy = sy << 3;
X  sx = sx << 3;
X  sy += 50;
X  sx += 24;
X
X  *ypos = (char)sy;
X  *xpos = (char)sx;
X  if(sx > 255)
X    {
X    *highx = 0x01;
X    }
X  else
X    {
X    *highx = 0x00;
X    }
X}
X
X/* erase() - clear screen */
Xerase()
X{
X  putchar((char)147);
X}
X
X/* standout() - reverse video */
Xstandout()
X{
X  putchar((char)18);
X}
X
X/* standend() - standard video */
Xstandend()
X{
X  putchar((char)146);
X}
X
X/* center() - center string on line */
Xcenter(y, str)
Xint y;
Xchar *str;
X{
X  int x;
X
X  x = 20 - strlen(str)/2;
X  move(y, x);
X  printf(str);
X}
X
X/* move() - cursor positioning */
Xmove(y, x)
Xint y, x;
X{
X  char ac, xc, yc;
X
X  /* check for bozo parameters */
X  if(y > 24) y = 24;
X  if(y < 0) y = 0;
X  if(x > 79) x = 79;
X  if(x < 0) x = 0;
X
X  xc = (char)y;
X  yc = (char)x;
X
X  sys(PLOT, &ac, &xc, &yc);
X}
X
X/* setborder() - set border color */
Xsetborder(color)
Xint color;
X{
X  char *border = 0xd020;
X
X  *border = (char)color;
X}
X
X/* setbg() - set background color */
Xsetbg(color)
Xint color;
X{
X  char *bg = 0xd021;
X
X  *bg = (char)color;
X}
X
X/* setfg() - set foreground color */
Xsetfg(color)
Xint color;
X{
X  static char colchars[16] =
X    {
X    144,  /* black */
X    5,    /* white */
X    28,   /* red */
X    159,  /* cyan */
X    156,  /* purple */
X    32,   /* green */
X    31,   /* blue */
X    158,  /* yellow */
X    129,  /* orange */
X    149,  /* brown */
X    150,  /* lt. red */
X    151,  /* grey 1 */
X    152,  /* grey 2 */
X    153,  /* lt. green */
X    154,  /* lt. blue */
X    155   /* grey 3 */
X    };
X
X  if(color < 0) color = 0;
X  if(color > 15) color = 15;
X
X  putchar(colchars[color]);
X}
X
X/* getstring() - get string with echo */
Xgetstring(s, len)
Xchar *s;
Xint len;
X{
X  int done, kbchar, ll;
X  char *ls;
X
X  done = 0;
X  ll = 0;
X  ls = s;
X  *ls = '\0';
X  updatecursor();
X  cursoron();
X
X  while(!done)
X    {
X    /* check for <C=> key */
X    if(chkstop())
X      {
X      cursoroff();
X      return(0);
X      }
X    if(charsinq() > 0)
X      {
X      kbchar = getkb();
X      /* only printable characters */
X      /* if(isprint(kbchar)) */
X      if((kbchar >= 32 && kbchar <= 95) ||
X         (kbchar >= 193 && kbchar <= 218))
X        {
X        *ls = (char)kbchar;
X        ls++;
X        ll++;
X        *ls = '\0';
X        putchar((char)kbchar);
X        updatecursor();
X        if(ll >= len)
X          {
X          done = 1;
X          }
X        }
X      else if(kbchar == 13)
X        {
X        done = 1;
X        }
X      else if(kbchar == 20)
X        {
X        /* backspace */
X        if(ll > 0)
X          {
X          ll--;
X          ls--;
X          *ls = '\0';
X          putchar((char)20);
X          updatecursor();
X          }
X        }
X      }
X    }
X  cursoroff();
X  return(strlen(s));
X}
X
X/* end of file */
SHAR_EOF
chmod 0664 screen.c || echo "restore of screen.c fails"
sed 's/^X//' << 'SHAR_EOF' > xmodem.c &&
X/* xmodem.c - xmodem protocol */
X/* W Mat Waites - Sept 1988 */
X
X#include <stdio.h>
X
X/* number of retries, timeouts */
X#define RETRY 5
X#define TOUT 2
X#define BTOUT 10
X
X/* protocol characters */
X#define SOH 0x01
X#define EOT 0x04
X#define ACK 0x06
X#define NAK 0x15
X#define CAN 0x18
X
X#define RECSIZE 128
X
Xchar *diskerr();
X
Xint rec;
Xint tries;
Xint timeout;
X
X/* buffer for data in/out */
Xchar buffer[132];
X
X/* sendfile() - send file via xmodem */
Xsendfile(fname, disk)
Xchar *fname;
Xint disk;
X{
X  int st;
X  int ch;
X  char errbuf[41];
X  char locname[21];
X  char *status = 0x0090;
X  FILE dfile;
X
X  rec = 1;
X
X  strcpy(locname, fname);
X  strcat(locname, ",r");
X
X  /* attempt to open file for read */
X  device(disk);
X  dfile = fopen(locname);
X
X  /* check for disk error */
X  strcpy(errbuf, diskerr(disk));
X  st = atoi(errbuf);
X  if(st >= 20)
X    {
X    close(dfile);
X    showerr(fname, errbuf);
X    return(0);
X    }
X
X  printf("%s opened\n", fname);
X
X  /* clear input buffer */
X  while(getserial() >= 0)
X    {
X    }
X
X  tries = RETRY;
X
X  for(;;)
X    {
X    printf("Synching...\n");
X    if(chkstop())
X      {
X      close(dfile);
X      return(0);
X      }
X    ch = getchtmo(BTOUT);
X    if(timeout)
X      {
X      printf("Timeout\n");
X      tries--;
X      if(tries > 0)
X        {
X        continue;
X        }
X      close(dfile);
X      return(0);
X      }
X    if(ch == NAK)
X      {
X      break;
X      }
X    printf("Strange char [%02x]\n", ch);
X    }
X
X  printf("Synched\n");
X
X  /* send the file */
X  while(fillbuf(dfile, buffer))
X    {
X    if(chkstop())
X      {
X      close(dfile);
X      return(0);
X      }
X    if(!txrec(buffer))
X      {
X      close(dfile);
X      return(0);
X      }
X    }
X
X  /* tell 'em we're done */
X  putserial(EOT);
X  for(;;)
X    {
X    ch = getchtmo(TOUT);
X    if(timeout)
X      {
X      putserial(EOT);
X      }
X    else
X      {
X      if(ch == ACK)
X        {
X        printf("sent EOT\n\n");
X        break;
X        }
X      }
X    }
X
X  close(dfile);
X  printf("%s transferred\n\n", fname);
X  return(1);
X}
X
X/* recvfile() - recv file via xmodem */
Xrecvfile(fname, disk)
Xchar *fname;
Xint disk;
X{
X  int st;
X  int ch;
X  int i;
X  char r1, r2, dt;
X  int response;
X  char rchk;
X  char locname[21];
X  char errbuf[41];
X  unsigned chksum;
X  FILE dfile;
X
X  rec = 1;
X
X  strcpy(locname, fname);
X  strcat(locname, ",w");
X
X  /* attempt to open file for write */
X  device(disk);
X  dfile = fopen(locname);
X
X  /* check for disk error */
X  strcpy(errbuf, diskerr(disk));
X  st = atoi(errbuf);
X  if(st >= 20)
X    {
X    close(dfile);
X    showerr(fname, errbuf);
X    return(0);
X    }
X
X  printf("%s opened\n", fname);
X
X  /* clear input queue */
X  while(getserial() >= 0)
X    {
X    }
X
X  /* transfer file */
X  response = NAK;
X  for(;;)
X    {
X    /* get a record */
X    printf("Record %3d ", rec);
X    tries = RETRY;
X    for(;;)
X      {
X      if(chkstop())
X        {
X        close(dfile);
X        return(0);
X        }
X      /* shake hands */
X      putserial(response);
X      /* get 1st char */
X      ch = getchtmo(TOUT);
X      if(timeout)
X        {
X        tries--;
X        if(tries > 0)
X          {
X          continue;  /* try again */
X          }
X        printf("Can't sync w/sender\n");
X
X        close(dfile);
X        return(0);
X        }
X      if(ch == SOH)  /* beg of data */
X        {
X        break;
X        }
X      else if(ch == EOT) /* done */
X        {
X        printf("got EOT\n\n");
X        close(dfile);
X        putserial(ACK);
X        printf("%s transferred\n\n",
X          fname);
X        return(1);
X        }
X      else if(ch == CAN)  /* cancelled */
X        {
X        close(dfile);
X        printf("Transfer cancelled!\n");
X        return(0);
X        }
X      else
X        {
X        printf("Strange char [%02x]\n", ch);
X        gobble();  /* clear any weirdness */
X        response = NAK;  /* and try again */
X        }
X      }
X
X    response = NAK;
X    r1 = getchtmo(TOUT);  /* record number */
X    if(timeout)
X      {
X      printf("TMO on recnum\n");
X      continue;
X      }
X    /* get 1's comp record number */
X    r2 = getchtmo(TOUT);
X    if(timeout)
X      {
X      printf("TMO on comp recnum\n");
X      continue;
X      }
X
X    /* get data */
X    chksum = 0;
X    for(i=0; i<RECSIZE; i++)
X      {
X      dt = getchtmo(TOUT);
X      if(timeout)
X        {
X        break;
X        }
X      buffer[i] = dt;
X      chksum += dt;
X      chksum &= 0xff;
X      }
X    /* check for data timeout */
X    if(timeout)
X      {
X      printf("TMO on data\n");
X      continue;
X      }
X
X    /* get checksum */
X    rchk = getchtmo(TOUT);
X    if(timeout)
X      {
X      printf("TMO on checksum\n");
X      continue;
X      }
X
X    /* compare rec num and 1's comp */
X    if((~r1 & 0xff) != (r2 & 0xff))
X      {
X      printf("Bad recnum's\n");
X      continue;
X      }
X
X    /* compare checksum and local one */
X    if(rchk != chksum)
X      {
X      printf("Bad checksum\n");
X      response = NAK;
X      continue;
X      }
X
X    if((r1 ==(rec-1) & 0xff)) /* dupe */
X      {
X      printf("Duplicate record\n");
X      response = ACK;
X      continue;
X      }
X
X    if(r1 != (rec & 0xff))
X      {
X      printf("Record numbering error\n");
X      close(dfile);
X      return(0);
X      }
X
X    rec++;
X
X    /* write data to file */
X    for(i=0; i<RECSIZE; i++)
X      {
X      putc(buffer[i], dfile);
X      }
X
X    printf("OK\n");
X    response = ACK;
X    }
X}
X
X/* showerr() - display disk error */
Xshowerr(fname, errmsg)
Xchar *fname;
Xchar *errmsg;
X{
X  erase();
X  move(11, 5);
X  printf("Error accessing %s", fname);
X  move(13, 5);
X  printf("[%s]", errmsg);
X  move(20, 5);
X}
X
X/* getchtmo() - get char w/timeout */
Xgetchtmo(timlen)
Xint timlen;
X{
X  int serchar;
X
X  timeout = 0;
X  setclock((unsigned)0); /* start timer */
X
X  for(;;)
X    {
X
X    serchar = getserial();
X    if(serchar >= 0)
X      {
X      return(serchar);
X      }
X
X    if(getclock() >= timlen)
X      {
X      timeout = 1;
X      return 0;
X      }
X    }
X}
X
X/* fillbuf() - get buffer of data */
Xfillbuf(filnum, buf)
Xint filnum;
Xchar buf[];
X{
X  int i;
X  int echk;
X  char *status = 0x0090;
X
X  for(i=0; i<RECSIZE; i++)
X    {
X    /* get a char from file */
X    if((echk=fgetc(filnum)) == EOF)
X      {
X      break;
X      }
X    buf[i] = echk;
X    }
X
X  if(i == 0) return 0;
X
X  /* set rest of buffer to CTRL-Z */
X  for(; i<RECSIZE; i++)
X    {
X    buf[i] = (char)26;
X    }
X  return(1);
X}
X
X/* txrec() - send rec, get response */
Xtxrec(buf)
Xchar buf[];
X{
X  int i;
X  int ch;
X  unsigned chksum;
X
X  tries = RETRY;
X
X  for(;;)
X    {
X    /* send record */
X
X    printf("Record %3d ", rec);
X    putserial(SOH);
X    putserial(rec);
X    putserial(~rec);
X    chksum = 0;
X    for(i=0; i<RECSIZE; i++)
X      {
X      putserial(buf[i]);
X      chksum += buf[i];
X      chksum &= 0xff;
X      }
X    putserial(chksum);
X
X    /* get response */
X    ch = getchtmo(BTOUT);
X    if(timeout)
X      {
X      tries--;
X      if(tries > 0)
X        {
X        printf("Retrying...\n");
X        continue;
X        }
X      printf("Timeout\n");
X      return(0);
X      }
X
X    /* analyze response */
X    if(ch == CAN)
X      {
X      printf("Cancelled\n");
X      return(0);
X      }
X    else if(ch == ACK)
X      {
X      printf("ACKed\n", rec);
X      break;
X      }
X    else
X      {
X      if(ch == NAK)
X        {
X        printf("NAKed\n", rec);
X        }
X      else
X        {
X        printf("Strange response\n");
X        }
X      tries--;
X      if(tries > 0)
X        {
X        continue;
X        }
X      printf("No more retries!\n");
X      return(0);
X      }
X    }
X  rec++;
X  return(1);
X}
X
X/* gobble() - gobble up stray chars */
Xgobble()
X{
X  unsigned gotone;
X
X  printf("\ngobbling\n");
X
X  sleep(2);
X
X  for(;;)
X    {
X    gotone = 0;
X    /* clear input queue */
X    while(getserial() >= 0)
X      {
X      gotone = 1;
X      }
X    if(gotone)
X      {
X      sleep(1);
X      }
X    else
X      {
X      return;
X      }
X    }
X}
X
X/* end of file */
SHAR_EOF
chmod 0664 xmodem.c || echo "restore of xmodem.c fails"
exit 0
-- 
W Mat Waites            | Oh, I used to be disgusted, but now I'm just amused.
gatech!emcard!mat       | Since their wings got rusted, you know the angels
8-5 ET: (404) 727-7197  |    wanna wear my red shoes  --E. Costello