void H19::processCharacter(char ch) { // mask off the high bit just in case, the real H19 would not have it set. ch &= 0x7f; if (mode_m == Normal) { switch (ch) { case ascii::NUL: case ascii::SOH: case ascii::STX: case ascii::ETX: case ascii::EOT: case ascii::ENQ: case ascii::ACK: case ascii::VT: case ascii::FF: case ascii::SO: case ascii::SI: case ascii::DLE: case ascii::DC1: case ascii::DC2: case ascii::DC3: case ascii::DC4: case ascii::NAK: case ascii::SYN: case ascii::ETB: case ascii::EM: case ascii::SUB: case ascii::FS: case ascii::GS: case ascii::RS: case ascii::US: case ascii::DEL: // From manual, these characters are not processed by the terminal break; case ascii::BEL: // Rings the bell. /// \todo - implement ringing bell. consoleLog("<BEL>"); break; case ascii::BS: // Backspace consoleLog("<BS>"); processBS(); break; case ascii::HT: // Horizontal Tab consoleLog("<TAB>"); processTAB(); break; case ascii::LF: // Line Feed processLF(); if (autoCR_m) { processCR(); } consoleLog("\n"); break; case ascii::CR: // Carriage Return processCR(); if (autoLF_m) { processLF(); } break; case ascii::CAN: // Cancel. break; case ascii::ESC: // Escape mode_m = Escape; consoleLog("<ESC>"); break; default: // if Printable character display it. #if CONSOLE_LOG fprintf(console_out, "%c", ch); #endif displayCharacter(ch); break; } } else if (mode_m == Escape) { // Assume we go back to Normal, so only the few that don't need to set the mode. mode_m = Normal; switch (ch) { case ascii::CAN: // CAN - Cancel // return to Normal mode, already set. break; case ascii::ESC: // Escape // From the ROM listing, stay in this mode. mode_m = Escape; break; // Cursor Functions case 'H': // Cursor Home posX_m = posY_m = 0; updated_m = true; break; case 'C': // Cursor Forward cursorForward(); break; case 'D': // Cursor Backward processBS(); // same processing as cursor backward break; case 'B': // Cursor Down cursorDown(); break; case 'A': // Cursor Up cursorUp(); break; case 'I': // Reverse Index reverseIndex(); break; case 'n': // Cursor Position Report cursorPositionReport(); break; case 'j': // Save cursor position saveCursorPosition(); break; case 'k': // Restore cursor position restoreCursorPosition(); break; case 'Y': // Direct Cursor Addressing mode_m = DCA_1; break; // Erase and Editing case 'E': // Clear Display clearDisplay(); break; case 'b': // Erase Beginning of Display eraseBOD(); break; case 'J': // Erase to End of Page eraseEOP(); break; case 'l': // Erase entire Line eraseEL(); break; case 'o': // Erase Beginning Of Line eraseBOL(); break; case 'K': // Erase To End Of Line eraseEOL(); break; case 'L': // Insert Line insertLine(); break; case 'M': // Delete Line deleteLine(); break; case 'N': // Delete Character deleteChar(); break; case '@': // Enter Insert Character Mode insertMode_m = true; break; case 'O': // Exit Insert Character Mode insertMode_m = false; break; // Configuration case 'z': // Reset To Power-Up Configuration reset(); break; case 'r': // Modify the Baud Rate /// \todo - determine if we should support this. debugss(ssH19, ERROR, "Error Unimplemented Modify Baud\n"); break; case 'x': // Set Mode mode_m = SetMode; break; case 'y': // Reset Mode mode_m = ResetMode; break; case '<': // Enter ANSI Mode /// \todo - implement ANSI mode. // ROM - just sets the mode debugss(ssH19, ERROR, "Error Entering ANSI mode - unsupported\n"); break; // Modes of operation case '[': // Enter Hold Screen Mode holdScreen_m = true; break; case '\\': // Exit Hold Screen Mode holdScreen_m = false; break; case 'p': // Enter Reverse Video Mode reverseVideo_m = true; break; case 'q': // Exit Reverse Video Mode reverseVideo_m = false; break; case 'F': // Enter Graphics Mode graphicMode_m = true; break; case 'G': // Exit Graphics Mode graphicMode_m = false; break; case 't': // Enter Keypad Shifted Mode keypadShifted_m = true; break; case 'u': // Exit Keypad Shifted Mode // ROM - just sets the mode keypadShifted_m = false; break; case '=': // Enter Alternate Keypad Mode // ROM - just sets the mode keypadShifted_m = true; break; case '>': // Exit Alternate Keypad Mode // ROM - just sets the mode keypadShifted_m = false; break; // Additional Functions case '}': // Keyboard Disable /// \todo - determine whether to do this. keyboardEnabled_m = false; break; case '{': // Keyboard Enable keyboardEnabled_m = true; break; case 'v': // Wrap Around at End Of Line wrapEOL_m = true; break; case 'w': // Discard At End Of Line wrapEOL_m = false; break; case 'Z': // Identify as VT52 (Data: ESC / K) debugss(ssH19, ERROR, "Identify request\n"); sendData(ascii::ESC); sendData('/'); sendData('K'); break; case ']': // Transmit 25th Line transmitLine25(); break; case '#': // Transmit Page transmitPage(); break; default: debugss(ssH19, WARNING, "Unhandled ESC: %d\n", ch); /// \todo - verify this is how the H19 ROM does it. break; } } else if (mode_m == SetMode) { mode_m = Normal; switch (ch) { case '1': // Enable 25th line // From the ROM, it erases line 25 on the enable, but here we erase on the disable. line25_m = true; break; case '2': // No key click keyClick_m = true; break; case '3': // Hold screen mode holdScreen_m = true; break; case '4': // Block Cursor cursorBlock_m = true; updated_m = true; break; case '5': // Cursor Off cursorOff_m = true; updated_m = true; break; case '6': // Keypad Shifted keypadShifted_m = true; break; case '7': // Alternate Keypad mode altKeypadMode_m = true; break; case '8': // Auto LF autoLF_m = true; break; case '9': // Auto CR autoCR_m = true; break; default: /// \todo Need to process ch as if none of this happened... debugss(ssH19, WARNING, "Invalid set Mode: %c\n", ch); break; } } else if (mode_m == ResetMode) { mode_m = Normal; switch (ch) { case '1': // Disable 25th line eraseLine(rowsMain_c); line25_m = false; updated_m = true; break; case '2': // key click keyClick_m = false; break; case '3': // Hold screen mode holdScreen_m = false; break; case '4': // Block Cursor cursorBlock_m = false; updated_m = true; break; case '5': // Cursor On cursorOff_m = false; updated_m = true; break; case '6': // Keypad Unshifted keypadShifted_m = false; break; case '7': // Exit Alternate Keypad mode altKeypadMode_m = false; break; case '8': // No Auto LF autoLF_m = false; break; case '9': // No Auto CR autoCR_m = false; break; default: /// \todo Need to process ch as if none of this happened... debugss(ssH19, WARNING, "Invalid reset Mode: %c\n", ch); break; } } else if (mode_m == DCA_1) { // From actual H19, once the line is specified, the cursor // immediately moves to that line, so no need to save the // position and wait for the entire command. /// \todo verify that this is the same on newer H19s. if (ch == ascii::CAN) { // cancel mode_m = Normal; } else { // \todo handle error conditions int pos = ch - 31; // verify valid Position if (((pos > 0) && (pos < (signed) rows_c)) || ((pos == (signed) rows_c) && (line25_m))) { posY_m = pos - 1; } else { /// \todo check to see how a real h19 handles this. debugss(ssH19, INFO, "DCA invalid Y: %d\n", pos); } mode_m = DCA_2; } } else if (mode_m == DCA_2) { if (ch == ascii::CAN) { // cancel mode_m = Normal; } else { int pos = ch - 31; if ((pos > 0) && (pos < 81)) { posX_m = pos - 1; } else { posX_m = (cols_c - 1); } updated_m = true; mode_m = Normal; } } }
// dest: MUST already contain a string, even if empty. // maxLineLength: Must include room for terminating NUL. // Returns TRUE if the user pressed Enter, FALSE if Break. // byte getLineFromUser(char *dest, word maxLineLength, word initIndex) { saveCursorPosition(); word index = initIndex; // offset of cursor in 'dest' // Print the existing string, if any. // word curLen = strlen(dest); putstr(dest, curLen); // Put the cursor at 'initIndex'. // restoreCursorPosition(); advanceCursor(initIndex); char key; for (;;) { animateCursor(); key = (char) tolower(inkey()); if (!key) continue; if (key == '\r' || key == '\n' || key == BREAK_CHAR) // if Enter (CoCo or PC) or Break { dest[curLen] = 0; break; } removeCursor(); if (key == CLEAR_CHAR) // if backspace { if (index > 0) { word numCharsToMove = curLen - index; moveBackwardOneByte(dest + index, numCharsToMove); --index; --curLen; putchar('\b'); byte finalX = textPosX, finalY = textPosY; putstr(dest + index, numCharsToMove); putchar(' '); // erase last char from screen moveCursor(finalX, finalY); // place cursor at erased char } continue; } if (key == SHIFT_CLEAR_CHAR) // if delete { if (index < curLen) { word numCharsToMove = curLen - index - 1; moveBackwardOneByte(dest + index + 1, numCharsToMove); --curLen; byte finalX = textPosX, finalY = textPosY; putstr(dest + index, numCharsToMove); putchar(' '); // erase last char from screen moveCursor(finalX, finalY); // place cursor at erased char } continue; } if (key == LEFT_CHAR) // if move cursor left { if (index > 0) { --index; putchar('\b'); } continue; } if (key == RIGHT_CHAR) // if move cursor right { if (index < curLen) { ++index; advanceCursor(1); } continue; } if (key == KILL_LINE_CHAR) // if shift-backspace { for (word i = 0; i < index; ++i) // back up cursor to beginning putchar('\b'); byte finalX = textPosX, finalY = textPosY; for (word i = 0; i < curLen; ++i) // erase entire line putchar(' '); moveCursor(finalX, finalY); // put cursor at beginning index = 0; curLen = 0; continue; } if (key < ' ' || key >= 127) continue; // ignore invalid characters if (curLen < maxLineLength - 1) // if still room { moveForwardOneByte(dest + index, curLen + 1 - index); // include terminating NUL dest[index] = key; ++curLen; saveCursorPosition(); putstr(dest + index, curLen - index); restoreCursorPosition(); advanceCursor(1); ++index; } } removeCursor(); return key != BREAK_CHAR; }