/* AmigaDisplay: A "smart display" terminal emulator for the Amiga * * Based on the AmigaTerm program by Michael Mounier, enhanced by Don Woods. * This version emulates either a "dumb terminal" or a DataMedia 2500. * Mounier's version was (c) 1985 but was freely distributed. Woods's * rewrite is (c) 1986 and is likewise available if due credit is given. * * The DM2500 supports a special command sequence used at Stanford to display * up to 128 different character glyphs, and also supports various special * interpretations applied to the ALT, AMIGA, and F# keys (again for use at * Stanford). These features, and the type of display being emulated, should * be fairly easy to change (e.g., if someone would rather emulate a VT100). * * The emulator also provides a variety of bell volumes (just for kicks), and * file send/capture, but NOT the so-called "xmodem" features. The send/ * capture stuff uses a requester to avoid mucking up the display region. */ /****************************************\ * This file is the main program (main.c) * \****************************************/ #include #include #include #include #include #include #include #include #include #include /* * Library definitions; shared with the other modules */ #define INTUITION_REV 1 #define GRAPHICS_REV 1 #define FONT_REV 1 struct IntuitionBase *IntuitionBase = NULL; struct GfxBase *GfxBase = NULL; struct DiskfontBase *DiskfontBase = NULL; /* * Functions imported from other modules */ /* in intuit.c */ extern InitWindowStuff(struct Window *); extern CleanUpWindow(struct Window *); extern FileMenu(struct Window *, int, FILE **, FILE **, int *); /* in dpy.c */ extern SetUpDisplay(struct Window *, int); extern char Emit(char); extern NeedQuote(char); /* in beep.c */ extern InitAudio(int); extern SetBeeper(int); extern Beep(); extern CleanUpBeeper(); /* * Globals and externals for using the serial port */ extern struct MsgPort *CreatePort(); static struct IOExtSer *inreq = NULL, *outreq = NULL; static char inbuf[2], outbuf[2]; /* * MAIN PROGRAM */ main() { char c; int buckied; /* see ToAsc for details */ int proceed = TRUE, fileFlags = 0; FILE *receive = NULL, *send = NULL; struct Window *mywindow; struct IntuiMessage *message; /* msg struct for GetMsg */ /* Bits set in fileFlags */ #define RAWCAPTURE 1 /* include ctrl chars in captured file */ #define SENDLFASCR 2 /* translate LFs to CRs when sending */ /* Emulator window structure */ static struct NewWindow newWindow = { 0, 0, 640, 200, 0, 1, RAWKEY | MENUPICK | GADGETUP | REQCLEAR, SMART_REFRESH | ACTIVATE | BORDERLESS, NULL, NULL, NULL, NULL, NULL, 100, 35, 640, 200, /* min/max dims, unused since sizegadget omitted */ WBENCHSCREEN}; IntuitionBase = (struct IntuitionBase *) OpenLibrary("intuition.library", INTUITION_REV); GfxBase = (struct GfxBase *) OpenLibrary("graphics.library", GRAPHICS_REV); DiskfontBase = (struct DiskfontBase *) OpenLibrary("diskfont.library", FONT_REV); if (IntuitionBase == NULL || GfxBase == NULL || DiskfontBase == NULL) GoAway("Can't open libraries", NULL); if ((mywindow = (struct Window *)OpenWindow(&newWindow)) == NULL) GoAway("Can't open window", NULL); /* Open serial device once and copy the initialised ioreq; */ /* that way we can have exclusive access with two ports */ inreq = (struct IOExtSer *)AllocMem(sizeof(*inreq), MEMF_PUBLIC|MEMF_CLEAR); inreq->IOSer.io_Message.mn_ReplyPort = CreatePort("SerialRead", 0); if (OpenDevice(SERIALNAME, NULL, inreq, NULL)) GoAway("Can't open Serial port", mywindow); inreq->io_SerFlags = SERF_SHARED | SERF_XDISABLED; inreq->IOSer.io_Command = SDCMD_SETPARAMS; inreq->io_ReadLen = inreq->io_WriteLen = 8; DoIO(inreq); inreq->IOSer.io_Length = 1; outreq = (struct IOExtSer *)AllocMem(sizeof(*outreq), MEMF_PUBLIC); movmem((char *)inreq, (char *)outreq, sizeof(*inreq)); outreq->IOSer.io_Message.mn_ReplyPort = CreatePort("SerialWrite", 0); inreq->IOSer.io_Command = CMD_READ; inreq->IOSer.io_Data = (APTR)inbuf; outreq->IOSer.io_Command = CMD_WRITE; outreq->IOSer.io_Data = (APTR)outbuf; if (InitWindowStuff(mywindow)) { CloseDevice(inreq); CloseDevice(outreq); GoAway("Not enough memory", mywindow); } /* Finish initialisation */ InitAudio(1); SetAPen(mywindow->RPort, 1); SetUpDisplay(mywindow, 1); SetBaud(1); BeginIO(inreq); while (proceed) { /* wait for something to do */ if (send == NULL) Wait((1 << inreq->IOSer.io_Message.mn_ReplyPort->mp_SigBit) | (1 << mywindow->UserPort->mp_SigBit)); if (CheckIO(inreq)) { /* receive a char from the host */ WaitIO(inreq); c = inbuf[0] & 0x7F; BeginIO(inreq); if (fileFlags & RAWCAPTURE) Emit(c); else c = Emit(c); /* Emit returns hex 80 if char is non-printing */ if (receive != NULL && c < 0x80) /* capture this character */ if ((c >= ' ' && c <= '~') || c == '\n' || c == '\t') putc(c, receive); /* always capture these chars */ else if (fileFlags & RAWCAPTURE) { if (c == 0x7F) fputs("^?", receive); else {putc('^', receive); putc(c+0x40, receive);} } } else if (send != NULL) { /* send file (only if host not talking) */ if ((c=getc(send)) != EOF) Ship(c == '\n' && (fileFlags & SENDLFASCR) ? '\r' : c); else { fclose(send); send = NULL; FileMenu(mywindow, 99, &receive, &send, &fileFlags); } } while (message = (struct IntuiMessage *)GetMsg(mywindow->UserPort)) { ULONG class; USHORT code, qual; class = message->Class; code = message->Code; qual = message->Qualifier; ReplyMsg(message); switch (class) { case RAWKEY: /* user has touched the keyboard */ if ((buckied = ToAsc(code, qual)) >= 0) { if (buckied & 0x100) Ship(0x80); else if (NeedQuote(c = buckied & 0x7F)) Ship(0); Ship(buckied & 0xFF); } else if (buckied != -99) { /* function key */ Ship(0); /* send NUL, number, CR */ Ship('0'+((-buckied)/10)); Ship('0'+((-buckied)%10)); Ship('\r'); } break; case MENUPICK: if (code != MENUNULL) switch (MENUNUM(code)) { case 0: FileMenu(mywindow, ITEMNUM(code), &receive, &send, &fileFlags); break; case 1: BaudMenu(code); break; case 2: SetUpDisplay(NULL, ITEMNUM(code)); break; case 3: SetBeeper(ITEMNUM(code)); if (Beep()) DisplayBeep(mywindow->WScreen); break; case 4: proceed = WindowMenu(mywindow, code); break; } break; } /* end of switch (class) */ } /* end of while (message) */ } /* end of while (proceed) */ /* It must be time to quit */ CloseDevice(inreq); CloseDevice(outreq); CleanUpWindow(mywindow); CleanUpBeeper(); GoAway(NULL, mywindow); /* exit will close send/receive files if open */ } /* end of main */ /* * Function to clean up however much stuff got started */ static GoAway(text, w) char *text; struct Window *w; { if (w) CloseWindow(w); if (inreq) { DeletePort(inreq->IOSer.io_Message.mn_ReplyPort); FreeMem(inreq, sizeof(*inreq)); } if (outreq) { DeletePort(outreq->IOSer.io_Message.mn_ReplyPort); FreeMem(outreq, sizeof(*outreq)); } if (IntuitionBase != NULL) CloseLibrary(IntuitionBase); if (GfxBase != NULL) CloseLibrary(GfxBase); if (DiskfontBase != NULL) CloseLibrary(DiskfontBase); if (text) {printf("ERROR: %s\n", text); exit(100);} exit(FALSE); } /* * Quickie to ship one char to serial port */ static Ship(c) char c; { outbuf[0] = c; DoIO(outreq); } /* * Menu routines (FileMenu is external) */ static BaudMenu(code) USHORT code; { AbortIO(inreq); SetBaud(ITEMNUM(code)); BeginIO(inreq); } static SetBaud(index) USHORT index; { switch (index) { case 0: inreq->io_Baud = 300; break; case 1: inreq->io_Baud = 1200; break; case 2: inreq->io_Baud = 2400; break; case 3: inreq->io_Baud = 4800; break; case 4: inreq->io_Baud = 9600; break; } inreq->IOSer.io_Command = SDCMD_SETPARAMS; DoIO(inreq); inreq->IOSer.io_Command = CMD_READ; } static WindowMenu(w, code) struct Window *w; USHORT code; { /* returns TRUE unless window is supposed to close */ switch (ITEMNUM(code)) { case 0: WindowToBack(w); break; case 1: WindowToFront(w); break; case 2: return(FALSE); } return(TRUE); } /* * Function to convert raw key data into ascii chars */ /* The system at Stanford uses 9-bit characters. The bottom 7 bits are normal * ascii, although there are visible characters associated with the ctrl chars. * (E.g., ^H prints as lambda, ^U as existential quantifier, ^A as down-arrow.) * The 0x80 bit and 0x100 bit are called "bucky bits" (see Hacker's Dictionary) * and are typically used to distinguish command chars from regular typein. * These bits are set by some extra shift keys on the Stanford keyboards, called * CONTROL and META. When talking to Stanford across an 8-bit serial line, * one sends CONTROL in the parity bit and META by a prefixed char (0x80). * This emulator interprets ALT as CONTROL, AMIGA as META, and CTRL as the * standard-ascii control key. If you don't need the bucky-bit features, just * don't hold down the ALT or AMIGA keys while typing, and you'll be fine. * * This function returns the 7-bit ascii code for the given keyboard action, * plus bucky-bits in 0x180. If the keyboard action is a no-op (e.g., key up, * or shift key down), it returns -99. Function keys return -(10+key#), or * -key# if shifted. */ /* qualifier bits */ #define LSHIFT (1<<0) #define RSHIFT (1<<1) #define SHLOCK (1<<2) #define CTL (1<<3) /* the real "ctrl" key */ #define LCTRL (1<<4) /* ALT keys set parity bit */ #define RCTRL (1<<5) #define LMETA (1<<6) /* Amiga keys set 9th bit */ #define RMETA (1<<7) static ToAsc(code, qual) USHORT code, qual; { char c; static char *map = "\ `1234567890-=\\?0qwertyuiop[]?123asdfghjkl;'??456\ ?zxcvbnm,./?.789 \b\t\n\r\33\177???-?\13\f"; if (code >= 0x50 && code <= 0x59) /* function keys */ return((qual & (LSHIFT|RSHIFT) ? -1 : -11) - (c = code&0xF)); if (code <= 0x4D) c = map[code]; else if (code == 0x5F) c = 0; /* HELP key translates into NUL */ else return(-99); if (c == '?') return(-99); if (qual & (LSHIFT|RSHIFT)) { if (c >= 'a' && c <= 'z') c -= 0x20; else if (c >= ',' && c <= '=') { code = c - ','; /* sidestep compiler bug re subscript exprs */ c = "<_>?)!@#$%^&*(::<+"[code]; } else if (c >= '[' && c <= '`') { code = c - '['; /* ditto */ c = "{|}^_~"[code]; } else if (c == '\'') c = '"'; } /* end shift */ else if ((qual & SHLOCK) && c >= 'a' && c <= 'z') c -= 0x20; if (qual & CTL) c &= 0x1F; return(c + (qual & (LCTRL|RCTRL) ? 0x80 : 0) + (qual & (LMETA|RMETA) ? 0x100 : 0)); } /* end of ToAsc */