/** * PLATOTerm64 - A PLATO Terminal for the Commodore 64 * Based on Steve Peltz's PAD * * Author: Thomas Cherryhomes * This file written by Steve Peltz. Copyright notice preserved. * and this code has been used with permission, and can be considered * public domain. * * protocol.c - Protocol decoder functions */ /* Copyright (c) 1990 by Steve Peltz */ #include "protocol.h" #define true 1 #define false 0 #define BSIZE 64 static padBool EscFlag; /* Currently in an escape sequence */ static Mode PMode, /* Mode */ CMode; /* Command */ static unsigned short SubMode; /* Block/Line modes */ static DataType PType, /* Mode type */ CType; /* Current type */ static unsigned short Phase; /* Phase of current type */ static padWord theWord; /* Data received for various data types */ static padByte theChar; static padByte rawChar; static padByte lastChar; static padRGB theColor; static unsigned short LowX, /* Previous coordinates received */ HiX, LowY, HiY; static padPt CurCoord; /* Current coordinate */ static padWord Margin; /* Margin for CR */ static padWord MemAddr; /* Load address for program data */ static unsigned short CharCnt; /* Current count for loading chars */ static charData Char; /* Character data */ static padByte charBuff[BSIZE]; static unsigned short charCount; /* Count of characters currently buffered */ static padPt charCoord; extern unsigned char terminal_get_features(void); extern unsigned char terminal_get_type(void); extern unsigned char terminal_get_subtype(void); extern unsigned char terminal_get_load_file(void); extern unsigned char terminal_get_configuration(void); extern unsigned short terminal_get_char_address(void); extern padByte terminal_mem_read(padWord addr); extern padByte terminal_ext_in(void); extern void screen_wait(void); extern void screen_beep(void); extern void io_send_byte(unsigned char b); extern void screen_block_draw(padPt* Coord1, padPt* Coord2); extern void screen_dot_draw(padPt* Coord); extern void screen_line_draw(padPt* Coord1, padPt* Coord2); extern void screen_char_draw(padPt* Coord, unsigned char* ch, unsigned char count); extern void screen_tty_char(padByte theChar); extern void screen_foreground(padRGB* theColor); extern void screen_background(padRGB* theColor); extern void screen_paint(padPt* Coord); extern void terminal_mem_load(padWord addr, padWord value); extern void terminal_char_load(padWord charnum, charData theChar); extern void terminal_mode_5(padWord value); extern void terminal_mode_6(padWord value); extern void terminal_mode_7(padWord value); extern void touch_allow(padBool allow); extern void terminal_ext_allow(padBool allow); extern void terminal_set_ext_in(padWord device); extern void terminal_set_ext_out(padWord device); extern void terminal_ext_out(padByte value); extern void screen_clear(void); extern void terminal_set_tty(void); extern void terminal_set_plato(void); #ifdef PROTOCOL_DEBUG extern void log(const char* format, ...); #endif static padByte PTAT0[128] = { /* PLATO to ASCII lookup table */ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, /* original mapping */ 0x38, 0x39, 0x26, 0x60, 0x0a, 0x5e, 0x2b, 0x2d, 0x13, 0x04, 0x07, 0x08, 0x7b, 0x0b, 0x0d, 0x1a, 0x02, 0x12, 0x01, 0x03, 0x7d, 0x0c, 0x83, 0x85, 0x3c, 0x3e, 0x5b, 0x5d, 0x24, 0x25, 0x5f, 0x7c, 0x2a, 0x28, 0x40, 0x27, 0x1c, 0x5c, 0x23, 0x7e, 0x17, 0x05, 0x14, 0x19, 0x7f, 0x09, 0x1e, 0x18, 0x0e, 0x1d, 0x11, 0x16, 0x00, 0x0f, 0x87, 0x88, 0x20, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x3d, 0x3b, 0x2f, 0x2e, 0x2c, 0x1f, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x29, 0x3a, 0x3f, 0x21, 0x22 }; static padByte PTAT1[128] = { 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, /* flow control mapping */ 0x38, 0x39, 0x26, 0x60, 0x09, 0x5e, 0x2b, 0x2d, 0x17, 0x04, 0x07, 0x08, 0x7b, 0x0b, 0x0d, 0x1a, 0x02, 0x12, 0x01, 0x03, 0x7d, 0x0c, 0x83, 0x85, 0x3c, 0x3e, 0x5b, 0x5d, 0x24, 0x25, 0x5f, 0x27, 0x2a, 0x28, 0x40, 0x7c, 0x1c, 0x5c, 0x23, 0x7e, 0x97, 0x84, 0x14, 0x19, 0x7f, 0x0a, 0x1e, 0x18, 0x0e, 0x1d, 0x05, 0x16, 0x9d, 0x0f, 0x87, 0x88, 0x20, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x3d, 0x3b, 0x2f, 0x2e, 0x2c, 0x1f, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x29, 0x3a, 0x3f, 0x21, 0x22 }; /* externally referenced variables */ padPt PLATOSize={512,512}; /* Logical screen size */ CharMem CurMem; /* Font to plot in */ padBool TTY, /* TTY mode */ FlowControl, /* Flow control on */ ModeBold, /* Character plotting conditions */ Rotate, Reverse; DispMode CurMode; /* Current PLATO plotting mode */ padBool FastText; /* Indicate to main program if text optimizations can be done. */ padBool NoEcho=padF; /*----------------------------------------------* * InitPAD, InitTTY, InitPLATO * * * * Called to initialize PAD variables. * * Calls back to external routines to * * set up proper plotting conditions. * *----------------------------------------------*/ void InitPAD (void) { InitTTY (); } void InitTTY (void) { charCount = 0; EscFlag = false; TTY = true; FastText = true; FlowControl = false; terminal_set_tty(); } void InitPLATO (void) { if (TTY) InitPLATOx (); } void InitPLATOx (void) { charCount = 0; EscFlag = false; TTY = false; terminal_set_plato (); SetMode (mAlpha, tByte); LowX = 0; HiX = 0; LowY = 0; HiY = 0; CurCoord.x = 0; CurCoord.y = 0; MemAddr = 0; CharCnt = 0; Margin = 0; ModeBold = false; Rotate = false; Reverse = false; FastText = true; CurMem = M0; CurMode = ModeRewrite; } /*----------------------------------------------* * Key * * * * Send a 10-bit internal key. If a keyset * * key, translate, else send as escaped * * sequence. * *----------------------------------------------*/ void Key (padWord theKey) { if (theKey >> 7) { io_send_byte (0x1b); io_send_byte (0x40 | (theKey & 0x3f)); io_send_byte (0x60 | ((theKey >> 6) & 0x0f)); } else { if (FlowControl == 0) theKey = PTAT0[theKey]; else theKey = PTAT1[theKey]; if (theKey & 0x80) { io_send_byte (0x1b); io_send_byte (theKey & 0x7f); } else io_send_byte (theKey); } } /*----------------------------------------------* * Touch * * * * Send a touch key (01yyyyxxxx). * *----------------------------------------------*/ void Touch(padPt* where) { io_send_byte(0x1b); io_send_byte(0x1f); io_send_byte(0x40 + (where->x & 0x1f)); io_send_byte(0x40 + ((where->x >> 5) & 0x0f)); io_send_byte(0x40 + (where->y & 0x1f)); io_send_byte(0x40 + ((where->y >> 5) & 0x0f)); Key(0x100 | ((where->x >> 1) & 0xF0) | ((where->y >> 5) & 0x0F)); } /*----------------------------------------------* * Ext * * * * Send an external key (10xxxxxxxx). * *----------------------------------------------*/ void Ext (padWord theKey) { Key (0x200 | (theKey & 0xFF)); } /*----------------------------------------------* * Echo * * * * Send an echo key (001xxxxxx). * *----------------------------------------------*/ void Echo (padWord theKey) { if (NoEcho==padT) return; Key (0x080 | theKey); } /*----------------------------------------------* * SetCommand, SetMode * * * * Set state machine variables. * *----------------------------------------------*/ void SetCommand (Mode theMode, DataType theType) { CMode = theMode; CType = theType; Phase = 0; } void SetMode (Mode theMode, DataType theType) { PMode = theMode; PType = theType; SubMode = 0; SetCommand (theMode, theType); } /*----------------------------------------------* * FixXY * * * * Move location by offset, then make sure * * it is still on the screen. * *----------------------------------------------*/ void FixXY (short DX, short DY) { if (ModeBold) { DX = DX * 2; DY = DY * 2; } if (Reverse) DX = -DX; if (Rotate) { CurCoord.x = CurCoord.x + DY; CurCoord.y = CurCoord.y + DX; } else { CurCoord.x = CurCoord.x + DX; CurCoord.y = CurCoord.y + DY; } if (CurCoord.x < 0) CurCoord.x += PLATOSize.x; else if (CurCoord.x >= PLATOSize.x) CurCoord.x -= PLATOSize.x; if (CurCoord.y < 0) CurCoord.y += PLATOSize.y; else if (CurCoord.y >= PLATOSize.y) CurCoord.y -= PLATOSize.y; } /*----------------------------------------------* * Superx, Subx, etc. * * * * Various character positioning commands. * *----------------------------------------------*/ void Superx (void) { FixXY (0, 5); } void Subx (void) { FixXY (0, -5); } void Marginx (void) { if (Rotate) Margin = CurCoord.y; else Margin = CurCoord.x; } void BSx (void) { FixXY (-8, 0); } void HTx (void) { FixXY (8, 0); } void LFx (void) { FixXY (0, -16); } void VTx (void) { FixXY (0, 16); } void FFx (void) { CurCoord.y = 0; CurCoord.x = 0; LFx (); } void CRx (void) { if (Rotate) CurCoord.y = Margin; else CurCoord.x = Margin; LFx (); } /*----------------------------------------------* * LoadCoordx * * * * Assemble completed coordinate. * *----------------------------------------------*/ void LoadCoordx (padPt * SetCoord) { SetCoord->x = (HiX << 5) + LowX; SetCoord->y = (HiY << 5) + LowY; } /*----------------------------------------------* * Blockx, Pointx, Linex, Alphax * * * * Plot the specified item at the * * current location. * *----------------------------------------------*/ void Blockx (void) { padPt NewCoord; if (SubMode == 0) { LoadCoordx (&CurCoord); SubMode = 1; } else { LoadCoordx (&NewCoord); screen_block_draw (&CurCoord, &NewCoord); SubMode = 0; CurCoord.y-=15; CurCoord.x+=16; } } void Pointx (void) { LoadCoordx (&CurCoord); screen_dot_draw (&CurCoord); } void Linex (void) { padPt OldCoord; if (SubMode == 0) { LoadCoordx (&CurCoord); SubMode = 1; } else { OldCoord.y = CurCoord.y; OldCoord.x = CurCoord.x; LoadCoordx (&CurCoord); screen_line_draw (&OldCoord, &CurCoord); } } void Alphax (void) { if (charCount == 0) charCoord = CurCoord; charBuff[charCount++] = theChar; HTx (); if (charCount >= BSIZE) { screen_char_draw (&charCoord, charBuff, charCount); charCount = 0; } } /*----------------------------------------------* * LoadEchox * * * * Echo responses to system. * *----------------------------------------------*/ void LoadEchox (void) { theWord &= 0x7f; switch (theWord) { case 0x52: FlowControl=true; if (FlowControl) Echo (0x53); /* flow control on */ else Echo (0x52); /* flow control not on */ break; case 0x60: /* Inquire about ascii specific features. */ Echo (terminal_get_features()); break; case 0x70: Echo (terminal_get_type ()); /* terminal type */ break; case 0x71: Echo (terminal_get_subtype()); /* subtype */ break; case 0x72: Echo (terminal_get_load_file ()); /* load file */ break; case 0x73: Echo (terminal_get_configuration ()); /* configuration */ break; case 0x7A: Key (0x3FF); /* send backout */ break; case 0x7B: screen_beep (); break; /* beep */ case 0x7D: Echo (terminal_mem_read (MemAddr) & 0x7F); break; default: Echo (theWord); /* normal echo */ break; } } void LoadAddrx (void) { MemAddr = theWord; CharCnt = 0; } void LoadCharx (void) { Char[CharCnt] = theWord; if (CharCnt < 7) CharCnt++; else { terminal_char_load ((((MemAddr - terminal_get_char_address ()) >> 4) & 0x7f), Char); CharCnt = 0; MemAddr += 16; } } void LoadMemx (void) { terminal_mem_load (MemAddr, theWord); MemAddr += 2; } void SSFx (void) { padByte device; device = (theWord >> 10) & 0xFF; if (device == 1) { terminal_ext_allow ((theWord >> 3) & 1); touch_allow ((theWord >> 5) & 1); } else if ((theWord >> 9) & 1) { terminal_set_ext_in (device); if (!((theWord >> 8) & 1)) Ext (terminal_ext_in ()); } else { terminal_set_ext_out (device); if (!((theWord >> 8) & 1)) terminal_ext_out (theWord & 0xFF); } } void Externalx (void) { terminal_ext_out ((theWord >> 8) & 0xFF); terminal_ext_out (theWord & 0xFF); } void GoMode (void) { switch (CMode) { case mBlock: Blockx (); break; case mPoint: Pointx (); break; case mLine: Linex (); break; case mAlpha: Alphax (); break; case mLoadCoord: LoadCoordx (&CurCoord); break; case mLoadAddr: LoadAddrx (); break; case mSSF: SSFx (); break; case mExternal: Externalx (); break; case mLoadEcho: LoadEchox (); break; case mLoadChar: LoadCharx (); break; case mLoadMem: LoadMemx (); break; case mMode5: terminal_mode_5 (theWord); break; case mMode6: terminal_mode_6 (theWord); break; case mMode7: terminal_mode_7 (theWord); break; case mFore: screen_foreground(&theColor); break; case mBack: screen_background(&theColor); break; case mPaint: screen_paint(&CurCoord); break; } CMode = PMode; CType = PType; Phase = 0; } void GoWord (void) { switch (Phase) { case 0: theWord = theChar & 0x3F; Phase = 1; break; case 1: theWord |= ((theChar & 0x3F) << 6); Phase = 2; break; case 2: theWord |= ((theChar & 0x3F) << 12); GoMode (); break; } } void GoCoord (void) { unsigned short CoordType, CoordValue; CoordValue = theChar & 0x1F; CoordType = ((theChar >> 5) & 3); switch (CoordType) { case 1: switch (Phase) { case 0: HiY = CoordValue; break; case 1: HiX = CoordValue; break; } Phase = 1; break; case 2: LowX = CoordValue; GoMode (); break; case 3: LowY = CoordValue; Phase = 1; break; } } void GoColor (void) { switch (Phase) { case 0: theColor.blue = (theChar & 0x3f); break; case 1: theColor.blue |= (theChar & 0x03) << 6; theColor.green = (theChar & 0x3c) >> 2; break; case 2: theColor.green |= (theChar & 0x0f) << 4; theColor.red = (theChar & 0x30) >> 4; break; case 3: theColor.red |= (theChar & 0x3f) << 2; break; } if (Phase < 3) Phase++; else GoMode (); } void GoPaint (void) { if (Phase == 0) Phase = 1; else GoMode (); } void DataChar (void) { switch (CType) { case tByte: Alphax (); break; case tWord: GoWord (); break; case tCoord: GoCoord (); break; case tColor: GoColor (); break; case tPaint: GoPaint (); break; } } void ShowPLATO (padByte *buff, unsigned short count) { while (count--) { theChar = *buff++; if (lastChar==0xFF && theChar==0xFF) { lastChar=0; } else { rawChar=theChar; theChar &=0x7F; if (TTY) { if (!EscFlag) screen_tty_char (theChar); else if (theChar == 0x02) InitPLATOx (); } else if (EscFlag) { switch (theChar) { case 0x03: InitTTY (); break; case 0x0C: screen_clear (); break; case 0x11: CurMode = ModeInverse; SetFast(); break; case 0x12: CurMode = ModeWrite; SetFast(); break; case 0x13: CurMode = ModeErase; SetFast(); break; case 0x14: CurMode = ModeRewrite; SetFast(); break; case 0x32: SetCommand (mLoadCoord, tCoord); break; case 0x40: Superx (); break; case 0x41: Subx (); break; case 0x42: CurMem = M0; break; case 0x43: CurMem = M1; break; case 0x44: CurMem = M2; break; case 0x45: CurMem = M3; break; case 0x4A: Rotate = false; SetFast(); break; case 0x4B: Rotate = true; SetFast(); break; case 0x4C: Reverse = false; SetFast(); break; case 0x4D: Reverse = true; SetFast(); break; case 0x4E: ModeBold = false; SetFast(); break; case 0x4F: ModeBold = true; SetFast(); break; case 0x50: SetMode (mLoadChar, tWord); break; case 0x51: SetCommand (mSSF, tWord); break; case 0x52: SetCommand (mExternal, tWord); break; case 0x53: SetMode (mLoadMem, tWord); break; case 0x54: SetMode (mMode5, tWord); break; case 0x55: SetMode (mMode6, tWord); break; case 0x56: SetMode (mMode7, tWord); break; case 0x57: SetCommand (mLoadAddr, tWord); break; case 0x59: SetCommand (mLoadEcho, tWord); break; case 0x5A: Marginx (); break; case 0x61: SetCommand (mFore, tColor); break; case 0x62: SetCommand (mBack, tColor); break; case 0x63: SetCommand (mPaint, tPaint); break; } } else if (theChar < 0x20) { if (charCount > 0) { screen_char_draw (&charCoord, charBuff, charCount); charCount = 0; } switch (theChar) { case 0x00: screen_wait(); case 0x08: BSx (); break; case 0x09: HTx (); break; case 0x0A: LFx (); break; case 0x0B: VTx (); break; case 0x0C: FFx (); break; case 0x0D: CRx (); break; case 0x19: SetMode (mBlock, tCoord); break; case 0x1C: SetMode (mPoint, tCoord); break; case 0x1D: SetMode (mLine, tCoord); break; case 0x1F: SetMode (mAlpha, tByte); break; } } else DataChar (); EscFlag = (theChar == 0x1B); lastChar=rawChar; } } if (charCount > 0) { screen_char_draw (&charCoord, charBuff, charCount); charCount = 0; } } /** * SetFast() * Toggle fast text output if mode = write or erase, and no rotate or bold */ void SetFast(void) { FastText = (((CurMode == ModeWrite) || (CurMode == ModeErase)) && ((Rotate == padF) && (ModeBold == padF))); }