/* * This handles vt100 commands. * */ #include #include #include "vt100.h" #define ESC "\x1b" #define send_str(x) send((const unsigned char *)x, sizeof(x)-1) extern void send(const unsigned char *, unsigned); extern unsigned char buffer[]; extern unsigned buffer_size; extern void ReverseScrollRegion(unsigned, unsigned); extern void ScrollRegion(unsigned, unsigned); extern void PrintChar(unsigned x, unsigned y, unsigned the_char, unsigned andMask, unsigned eorMask); extern void FillChar(unsigned the_char, unsigned andMask, unsigned eorMask); extern void ClearScreen(void); extern void ClearScreen2(unsigned start, unsigned end); extern void ClearLine(unsigned line); extern void ClearLine2(unsigned line, unsigned start, unsigned end); extern void SetCursor(unsigned x, unsigned y); extern void HideCursor(void); extern void ShowCursor(unsigned x, unsigned y); extern pascal void SysBeep2(Word) inline(0x3803,dispatcher); int __x; int __y; #define __brk() asm { brk 0xea } static unsigned saved_cursor[2]; // saved cursor position static unsigned window[2]; static unsigned and_mask; static unsigned xor_mask; static unsigned DECANM = 1; static unsigned DECAWM = 1; static unsigned DECCKM = 0; static unsigned DECKPAM = 0; static unsigned DECCOLM = 80; static unsigned DECOM = 0; static unsigned DECSCNM = 0; static unsigned LNM = 0; unsigned tabs[9] = { 0 }; static unsigned parms[10]; static unsigned parm_count; static unsigned private; static unsigned state; static unsigned initial_state = vtDEFAULT; void vt100_init(unsigned flags) { __x = 0; __y = 0; saved_cursor[0] = 0; saved_cursor[1] = 0; window[0] = 0; window[1] = 23; and_mask = 0xffff; xor_mask = 0x0000; initial_state = flags; if (flags == -1) { DECANM = 1; DECAWM = 1; DECCKM = 0; DECKPAM = 0; DECCOLM = 80; DECOM = 0; DECSCNM = 0; LNM = 0; } else { DECANM = flags & vtDECANM; DECAWM = flags & vtDECAWM; DECCKM = flags & vtDECCKM; DECKPAM = flags & vtDECKPAM; DECCOLM = flags & vtDECCOLM ? 132 : 80; DECOM = flags & vtDECOM; DECSCNM = flags & vtDECSCNM; LNM = flags & vtLNM; } tabs[0] = 0x0100; tabs[1] = 0x0101; tabs[2] = 0x0101; tabs[3] = 0x0101; tabs[4] = 0x0101; parms[0] = 0; parm_count = 0; private = 0; state = 0; HideCursor(); } #if 0 void dump(const char * buffer, word cnt, int impl) { int i; char tmp[] = " xx x"; char c; if ( impl) WriteCString("Unimplemented: "); for (i = 0; i < cnt; i++) { c = buffer[i]; tmp[1] = "0123456789abcdef"[c >> 4]; tmp[2] = "0123456789abcdef"[c & 0x0f]; tmp[4] = isprint(c) ? c : '.'; WriteCString(tmp); } WriteCString("\r\n"); } #endif static void cursor_left(void){ unsigned n = parms[0]; if (n == 0) n = 1; __x -= n; if (__x < 0) __x = 0; } static void cursor_right(void){ unsigned n = parms[0]; if (n == 0) n = 1; // edge case if (__x == 80) return; __x += n; if (__x >= 80) n = 79; } static void cursor_up(void) { unsigned n = parms[0]; if (n == 0) n = 1; /* can't escape scrolling region */ if (__y >= window[0]) { __y -= n; if (__y < window[0]) __y = window[0]; } else { __y -= n; if (__y < 0) __y = 0; } } static void cursor_down(void) { unsigned n = parms[0]; if (n == 0) n = 1; /* can't escape scrolling region */ if (__y <= window[1]) { __y += n; if (__y > window[1]) __y = window[1]; } else { __y += n; if (__y >= 24) __y = 23; } } static void cursor_position(void) { unsigned y = parms[0]; unsigned x = parms[1]; if (y) --y; if (x) --x; if (x >= 80) x = 79; if (DECOM) { y = window[0] + y; if (y > window[1]) y = window[1]; } else { if (y >= 24) y = 23; } __x = x; __y = y; } static void cursor_position_vt52(void) { unsigned y = parms[0]; unsigned x = parms[1]; if (y) --y; if (x) --x; // if x or y are out of range, do not move them. if (x < 80) __x = x; if (DECOM) { y = window[0] + y; if (y <= window[1]) __y = y; } else { if (y < 24) __y = y; } } static void scrolling_region(void) { unsigned top = parms[0]; unsigned bottom = parms[1]; if (top) --top; if (!bottom) bottom = 24; --bottom; if ((top < bottom) && (bottom < 24)) { window[0] = top; window[1] = bottom; __y = DECOM ? top : 0; __x = 0; } } static void set_attributes(void) { unsigned i; for (i = 0; i < parm_count; ++i) { switch(parms[i]) { case 0: // all attributes off and_mask = 0xffff; xor_mask = 0x0000; break; case 1: //bold and_mask = 0xaaaa; break; case 4: //underscore case 5: // blink and_mask = 0x5555; break; case 7://reverse video xor_mask = 0xffff; break; } } } static void set_mode(void) { unsigned i; for (i = 0; i < parm_count; ++i) { switch(parms[i]) { case 1: if (private) DECCKM = 1; break; case 2: if (private) DECANM = 1; break; case 3: if (private) DECCOLM = 132; break; case 6: if (private) { DECOM = 1; __x = 0; __y = window[0]; } break; case 7: if (private) DECAWM = 1; break; case 20: if (!private) LNM = 1; break; } } } static void reset_mode(void) { unsigned i; for (i = 0; i < parm_count; ++i) { switch(parms[i]) { case 1: if (private) DECCKM = 0; break; case 2: if (private) DECANM = 0; break; case 3: if (private) DECCOLM = 80; break; case 6: if (private) { DECOM = 0; __x = 0; __y = 0; } break; case 7: if (private) DECAWM = 0; break; case 20: if (!private) LNM = 0; break; } } } static void save_cursor(void) { saved_cursor[0] = __y; saved_cursor[1] = __x; } static void restore_cursor(void) { __y = saved_cursor[0]; __x = saved_cursor[1]; // vt220+ also saves/restores DECAWM and DECOM... } static void tab(void) { unsigned chunk; unsigned mask; if (__x >= 80) return; __x = __x + 1; chunk = __x >> 0x4; mask = 1 << (__x & 0x0f); while (chunk < 5) { unsigned bits = tabs[chunk]; if (!bits) { __x += 16; } else { do { if (bits & mask) return; ++__x; mask <<= 1; } while (mask); } mask = 1; ++chunk; } // nothing... go to right margin. __x = 79; } static void set_tab(void) { unsigned chunk; unsigned mask; if (__x >= 80) return; chunk = __x >> 0x04; mask = 1 << (__x & 0x0f); tabs[chunk] |= mask; } static void clear_tabs(void) { unsigned i; unsigned chunk; unsigned mask; for (i = 0; i < parm_count; ++i) { switch(parms[i]) { case 0: if (__x < 80) { chunk = __x >> 0x04; mask = 1 << (__x & 0x0f); tabs[chunk] &= ~mask; } break; case 3: tabs[0] = 0; tabs[1] = 0; tabs[2] = 0; tabs[3] = 0; tabs[4] = 0; break; } } } static void linefeed(void) { if (__y == window[1]) { ScrollRegion(window[0], window[1]+1); } else ++__y; } static void reverse_linefeed(void) { if (__y == window[0]) { ReverseScrollRegion(window[0], window[1]+1); } else --__y; } static void erase_line(void) { unsigned i; for (i = 0; i < parm_count; ++i) { switch(parms[i]) { case 0: ClearLine2(__y, __x, 80); break; case 1: ClearLine2(__y, 0, __x + 1); break; case 2: ClearLine(__y); break; } } } static void erase_screen(void) { unsigned i; for (i = 0; i < parm_count; ++i) { switch(parms[i]) { case 0: ClearLine2(__y, __x, 80); ClearScreen2(__y + 1, 24); break; case 1: ClearLine2(__y, 0, __x + 1); ClearScreen2(0, __y); break; case 2: ClearScreen(); break; } } } /* Device Status Report */ static void dsr(void) { unsigned i; for (i = 0; i < parm_count; ++i) { switch(parms[i]) { case 0: send_str(ESC "[0n"); break; case 5: { /* cursor position */ static char buffer[16]; unsigned i = 15; unsigned x = __x + 1; unsigned y = __y + 1; if (DECOM) y += window[0]; buffer[i--] = ']'; while(x) { buffer[i--] = '0' + (x % 10); x = x / 10; } buffer[i--] = ';'; while(y) { buffer[i--] = '0' + (y % 10); y = y / 10; } buffer[i--] = '['; buffer[i] = 0x1b; send(buffer + i, 16 - i); break; } } } } enum { st_text, st_escape, st_escape_v52, st_dca1, st_dca2, st_pound, st_rparen, st_lparen, st_lbracket, st_lbracket2, st_parm, }; static void do_control(char c) { switch(c) { case 005: /* ENQ */ break; case 007: /* BEL */ SysBeep2(0); break; case 010: /* BS */ if (__x) --__x; break; case 011: /* HT */ tab(); break; case 012: /* LF */ case 013: /* VT */ case 014: /* FF */ linefeed(); if (LNM) __x = 0; break; case 015: /* CR */ __x = 0; break; case 016: /* SO */ break; case 017: /* SI */ break; case 021: /* XON */ break; case 023: /* XOFF */ break; case 030: /* CAN */ case 032: /* SUB */ state = st_text; break; case 033: /* ESC */ switch (state) { case st_text: state = DECANM ? st_escape : st_escape_v52; break; case st_escape_v52: case st_escape: break; default: state = st_text; } break; } } void vt100_process(const unsigned char *buffer, unsigned buffer_size) { unsigned i; HideCursor(); for (i = 0; i < buffer_size; ++i) { unsigned char c = buffer[i] & 0x7f; if (c == 0 || c == 0x7f) continue; if (c < 0x20) { do_control(c); continue; } switch (state) { case st_text: if (__x == 80) { if (DECAWM) { __x = 0; if (__y == window[1]) { ScrollRegion(window[0], window[1]+1); } else ++__y; } else { __x = 79; } } PrintChar(__x, __y, c, and_mask, xor_mask); ++__x; break; case st_escape: switch (c) { case '#': state = st_pound; break; case '(': state = st_lparen; break; case ')': state = st_rparen; break; case '[': state = st_lbracket; break; case 'E': /* next line */ __x = 0; /* drop through */ case 'D': /* index */ linefeed(); break; case 'H': set_tab(); break; case 'M': /* reverse index */ reverse_linefeed(); break; case 'Z': send_str(ESC "[?1;0c"); break; case '7': save_cursor(); break; case '8': restore_cursor(); break; case '=': DECKPAM = 1; break; case '>': DECKPAM = 0; break; case 'c': vt100_init(initial_state); ClearScreen(); break; case '1': case '2': /* vt105 graphic stuff */ break; default: break; } if (state == st_escape) state = st_text; break; case st_pound: /* #8 -> fill with Es */ if (c == '8') { FillChar('E', 0xffff, 0x0000); } state = st_text; break; case st_lparen: case st_rparen: state = st_text; break; break; case st_lbracket: private = 0; parm_count = 0; parms[0] = 0; parms[1] = 0; if (c == '?') { private = 1; state = st_parm; continue; } if (isdigit(c)) { parms[0] = c - '0'; state = st_parm; break; } if (c == ';') { ++parm_count; parms[parm_count] = 0; state = st_parm; break; } /* fall through */ case st_lbracket2: lbracket2: ++parm_count; switch (c) { case 'A': cursor_up(); break; case 'B': cursor_down(); break; case 'C': cursor_right(); break; case 'D': cursor_left(); break; case 'H': case 'f': cursor_position(); break; case 'K': erase_line(); break; case 'J': erase_screen(); break; case 'c': if (parms[0] == 0) send_str(ESC "[?1;0c"); break; case 'g': clear_tabs(); break; case 'h': set_mode(); break; case 'l': reset_mode(); break; case 'm': set_attributes(); break; case 'n': dsr(); break; case 'r': scrolling_region(); break; } state = st_text; break; case st_parm: if (isdigit(c)) { parms[parm_count] *= 10; parms[parm_count] += c - '0'; continue; } if (c == ';') { if (parm_count < 10) ++parm_count; parms[parm_count] = 0; continue; } state = st_lbracket2; goto lbracket2; case st_escape_v52: parm_count = 0; parms[0] = 0; if (c == 'Y') { state = st_dca1; break; } switch (c) { case 'A': cursor_up(); break; case 'B': cursor_down(); break; case 'C': cursor_right(); break; case 'D': cursor_left(); break; case 'F': case 'G': break; /* graphics mode */ case 'H': cursor_position(); break; case 'I': reverse_linefeed(); break; case 'J': erase_screen(); break; case 'K': erase_line(); break; case 'Z': send_str(ESC "/Z"); break; case '=': DECKPAM = 1; break; case '>': DECKPAM = 0; break; case '<': DECANM = 1; break; } state = st_text; break; case st_dca1: parms[0] = c - 0x20; state = st_dca2; break; case st_dca2: parms[1] = c - 0x20; cursor_position_vt52(); state = st_text; break; } } ShowCursor(__x, __y); } /* adb codes for extended keys. passed as-is with keypad bit set */ /* nb - kegs gobbles up fkeys for his own use */ enum { kVK_F17 = 0x40, kVK_VolumeUp = 0x48, kVK_VolumeDown = 0x49, kVK_Mute = 0x4A, kVK_F18 = 0x4F, kVK_F19 = 0x50, kVK_F20 = 0x5A, kVK_F5 = 0x60, kVK_F6 = 0x61, kVK_F7 = 0x62, kVK_F3 = 0x63, kVK_F8 = 0x64, kVK_F9 = 0x65, kVK_F11 = 0x67, kVK_F13 = 0x69, kVK_F16 = 0x6A, kVK_F14 = 0x6B, kVK_F10 = 0x6D, kVK_F12 = 0x6F, kVK_F15 = 0x71, kVK_Help = 0x72, kVK_Home = 0x73, kVK_PageUp = 0x74, kVK_ForwardDelete = 0x75, kVK_F4 = 0x76, kVK_End = 0x77, kVK_F2 = 0x78, kVK_PageDown = 0x79, kVK_F1 = 0x7A }; // // remap the iigs key to a vt100 code (if necessary) and send it out. /* * see Apple IIgs Toolbox Ref Volume 1, 3-22. (ADB codes) */ void vt100_event(EventRecord *event) { unsigned char *cp; int len = 0; unsigned char key; word mods; key = event->message; mods = event->modifiers; /* iigs -> vt100 * clear -> PF1 * = -> PF2 * / -> PF3 * * -> PF4 * + -> - * - -> , * . -> . * * * win32 layout: * numlock -> PF1 * / -> PF2 * * -> PF3 * - -> PF4 * shift+ -> - * + -> , * * ------ * option - , 1 2 3 4 to use normal keys as keypad. */ if (mods & keyPad) { switch (key) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': case '.': case '-': case ',': { static unsigned char buffer[3] = { 0x1b, 'x', 'x' }; if (DECKPAM) { cp = buffer; len = 3; buffer[2] = key | 0x40; buffer[1] = DECANM ? 'O' : '?'; } else { len = 1; cp = &key; } } break; case 0x0d: // enter if (DECKPAM) { cp = DECANM ? ESC "OM" : ESC "?M"; len = 3; } else goto enter; break; case kVK_F1: // f1 if (DECANM) { cp = ESC "OP"; len = 3; } else { cp = ESC "P"; len = 2; } break; case kVK_F2: // f2 if (DECANM) { cp = ESC "OQ"; len = 3; } else { cp = ESC "Q"; len = 2; } break; case kVK_F3: // f3 if (DECANM) { cp = ESC "OR"; len = 3; } else { cp = ESC "R"; len = 2; } break; case kVK_F4: // f4 if (DECANM) { cp = ESC "OS"; len = 3; } else { cp = ESC "S"; len = 2; } break; /* Real IIgs keyboard - clear = 0x18, =, /, * for PF1-4? */ case 0x18: // clear - PF1 on IIgs keyboard if (0) { if (DECANM) { cp = ESC "OP"; len = 3; } else { cp = ESC "P"; len = 2; } } break; case '=': // PF2 on IIgs keyboard if (0) { if (DECANM) { cp = ESC "OQ"; len = 3; } else { cp = ESC "Q"; len = 2; } } break; case '/': // PF3 on IIgs keyboard if (0) { if (DECANM) { cp = ESC "OR"; len = 3; } else { cp = ESC "R"; len = 2; } } else { len = 1; cp = &key; } break; case '*': // PF4 on IIgs keyboard if (0) { if (DECANM) { cp = ESC "OS"; len = 3; } else { cp = ESC "S"; len = 2; } } else { len = 1; cp = &key; } break; case '+': // send as normal key. len = 1; cp = &key; break; default: return; } } else if (mods & controlKey) { cp = &key; len = 1; } else { switch(key) { case 0x0d: enter: if (LNM) { cp = "\r\n"; len = 2; } else { cp = "\r"; len = 1; } break; case 0x08: // <--- arrow // todo -- cursor keys if (DECANM) { cp = DECCKM ? ESC "OD" : ESC "[D"; len = 3; } else { cp = ESC "D"; len = 2; } break; case 0x0A: // down arrow if (DECANM) { cp = DECCKM ? ESC "OB" : ESC "[B"; len = 3; } else { cp = ESC "B"; len = 2; } break; case 0x0B: // up arrow if (DECANM) { cp = DECCKM ? ESC "OA" : ESC "[A"; len = 3; } else { cp = ESC "A"; len = 2; } break; case 0x15: // ---> arrow if (DECANM) { cp = DECCKM ? ESC "OC" : ESC "[C"; len = 3; } else { cp = ESC "C"; len = 2; } break; // backspace to delete char case 0x7f: // delete cp = "\x08"; len = 1; break; default: if (key < 0x7f) { cp = &key; len = 1; } } } if (len) send(cp, len); }