diff --git a/2Term.xcodeproj/project.pbxproj b/2Term.xcodeproj/project.pbxproj index bc07f8a..54f5ed9 100644 --- a/2Term.xcodeproj/project.pbxproj +++ b/2Term.xcodeproj/project.pbxproj @@ -47,6 +47,7 @@ B6C704EF15CCC64100CC0401 /* titlebar-center@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = B6C704EC15CCC64100CC0401 /* titlebar-center@2x.png */; }; B6C704F015CCC64100CC0401 /* titlebar-left@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = B6C704ED15CCC64100CC0401 /* titlebar-left@2x.png */; }; B6C704F115CCC64100CC0401 /* titlebar-right@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = B6C704EE15CCC64100CC0401 /* titlebar-right@2x.png */; }; + B6D1CD071E577E7D00C4A6BC /* PTSE.mm.ragel in Sources */ = {isa = PBXBuildFile; fileRef = B612F45D12DD5DF1005D1B77 /* PTSE.mm.ragel */; }; B6EBE2B511E0EA9100EA0458 /* CharacterGenerator.mm in Sources */ = {isa = PBXBuildFile; fileRef = B6EBE2B411E0EA9100EA0458 /* CharacterGenerator.mm */; }; B6ECFF271D2EEA2B00871A81 /* TextLabel.m in Sources */ = {isa = PBXBuildFile; fileRef = B6ECFF261D2EEA2B00871A81 /* TextLabel.m */; }; /* End PBXBuildFile section */ @@ -119,7 +120,7 @@ B612F45A12DD5DF1005D1B77 /* GNOConsole.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GNOConsole.h; sourceTree = ""; }; B612F45B12DD5DF1005D1B77 /* GNOConsole.mm.ragel */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.objcpp; fileEncoding = 4; path = GNOConsole.mm.ragel; sourceTree = ""; }; B612F45C12DD5DF1005D1B77 /* PTSE.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PTSE.h; sourceTree = ""; }; - B612F45D12DD5DF1005D1B77 /* PTSE.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = PTSE.mm; sourceTree = ""; }; + B612F45D12DD5DF1005D1B77 /* PTSE.mm.ragel */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = PTSE.mm.ragel; sourceTree = ""; }; B612F45E12DD5DF1005D1B77 /* VT05.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = VT05.h; sourceTree = ""; }; B612F45F12DD5DF1005D1B77 /* VT05.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = VT05.mm; sourceTree = ""; }; B612F46012DD5DF1005D1B77 /* VT100.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = VT100.h; sourceTree = ""; }; @@ -295,16 +296,16 @@ B612F45512DD5DF1005D1B77 /* Emulators */ = { isa = PBXGroup; children = ( - B612F45612DD5DF1005D1B77 /* Apple80.h */, - B612F45712DD5DF1005D1B77 /* Apple80.mm.ragel */, B612F45812DD5DF1005D1B77 /* Emulator.h */, B612F45912DD5DF1005D1B77 /* EmulatorManager.mm */, + B612F45612DD5DF1005D1B77 /* Apple80.h */, + B612F45712DD5DF1005D1B77 /* Apple80.mm.ragel */, B612F45A12DD5DF1005D1B77 /* GNOConsole.h */, B612F45B12DD5DF1005D1B77 /* GNOConsole.mm.ragel */, B6C173901D31D2B80024E360 /* GSOSConsole.h */, B6C173911D31D2B80024E360 /* GSOSConsole.mm.ragel */, B612F45C12DD5DF1005D1B77 /* PTSE.h */, - B612F45D12DD5DF1005D1B77 /* PTSE.mm */, + B612F45D12DD5DF1005D1B77 /* PTSE.mm.ragel */, B612F45E12DD5DF1005D1B77 /* VT05.h */, B612F45F12DD5DF1005D1B77 /* VT05.mm */, B612F46012DD5DF1005D1B77 /* VT100.h */, @@ -440,6 +441,7 @@ B675F4A91E561D20004B0D9C /* Apple80.mm.ragel in Sources */, B675F4AA1E562159004B0D9C /* GNOConsole.mm.ragel in Sources */, B675F4AC1E56A7F2004B0D9C /* GSOSConsole.mm.ragel in Sources */, + B6D1CD071E577E7D00C4A6BC /* PTSE.mm.ragel in Sources */, 8D11072D0486CEB800E47090 /* main.m in Sources */, 256AC3DA0F4B6AC300CF3369 /* TwoTermAppDelegate.mm in Sources */, B676063B11DEAD3500D6B66C /* TermWindowController.mm in Sources */, diff --git a/Emulators/PTSE.h b/Emulators/PTSE.h index 105fc14..d75bba7 100644 --- a/Emulators/PTSE.h +++ b/Emulators/PTSE.h @@ -17,15 +17,11 @@ @interface PTSE : NSObject { - unsigned _state; - - iPoint _dca; - uint8_t _repeatChar; - -#ifdef __cplusplus - TextPort _textPort; -#endif + context _context; + + unsigned cs; + int _scratch[4]; } diff --git a/Emulators/PTSE.mm b/Emulators/PTSE.mm deleted file mode 100644 index eaacd18..0000000 --- a/Emulators/PTSE.mm +++ /dev/null @@ -1,376 +0,0 @@ -// -// PTSE.mm -// 2Term -// -// Created by Kelvin Sherlock on 7/9/2010. -// Copyright 2010 __MyCompanyName__. All rights reserved. -// - -#include - - -#import "PTSE.h" - -#include "OutputChannel.h" -#include "Screen.h" - - - -@implementation PTSE - -enum { - StateText, - - StateDCAX, - StateDCAY, - - StateRepeatChar, - StateRepeatCount, - - StateTone1, - StateTone2, - StateToneDuration - -}; - -+(void)load -{ - [EmulatorManager registerClass: self]; -} - -+(NSString *)name -{ - return @"Proterm Special Emulation"; -} - --(NSString *)name -{ - return @"Proterm Special Emulation"; -} - --(const char *)termName -{ - return "proterm-special"; -} - --(void)reset: (Screen *)screen -{ - [self reset]; - - if (screen) - { - screen->setFlag(Screen::FlagNormal); - screen->setTextPort(_textPort); - screen->erase(Screen::EraseAll); - } - - -} --(void)reset -{ - struct winsize ws = [self defaultSize]; - _state = StateText; - - _textPort.cursor = iPoint(0, 0); - _textPort.frame = iRect(0, 0, ws.ws_col, ws.ws_row); - - _textPort.scroll = true; - _textPort.advanceCursor = true; - _textPort.clampX = true; - _textPort.clampY = true; - _textPort.leftMargin = TextPort::MarginWrap; - _textPort.rightMargin = TextPort::MarginWrap; - -} - --(BOOL)resizable -{ - return NO; -} - --(struct winsize)defaultSize -{ - struct winsize ws = { 24, 80, 0, 0 }; - - return ws; -} - --(id)init -{ - [self reset]; - return self; -} - --(void)initTerm: (struct termios *)term -{ - // Control-U is used by the up-arrow key. - term->c_cc[VKILL] = CTRL('X'); -} - --(void)processCharacter:(uint8_t)c screen:(Screen *)screen output:(OutputChannel *)output -{ - - if (_state == StateText) - { - switch (c) - { - case CTRL('N'): - //Set: inverse off, mousetext off. - screen->setFlag(Screen::FlagNormal); - break; - case CTRL('O'): - //Set: inverse on, mousetext off. - screen->setFlag(Screen::FlagInverse); - break; - case CTRL('P'): - //Set inverse off, mousetext on. - screen->setFlag(Screen::FlagMouseText | Screen::FlagInverse); - break; - - - case CTRL('H'): - //Move cursor left one character. - screen->decrementX(&_textPort); - break; - case CTRL('U'): - //Move cursor right one character. - screen->incrementX(&_textPort); - break; - case CTRL('K'): - //Move cursor up one line. - screen->decrementY(&_textPort); - break; - case CTRL('J'): - //Move cursor down one line. - //screen->incrementY(); - screen->lineFeed(&_textPort); - break; - case CTRL('I'): - //Move cursor to next tab stop (every 8 chars). - screen->tabTo(&_textPort, (_textPort.cursor.x + 8) & ~0x07); - break; - case CTRL('A'): - //Move cursor to beginning of line. - screen->setX(&_textPort, 0); - break; - case CTRL('B'): - //Move cursor to end of line. - screen->setX(&_textPort, _textPort.frame.width() - 1); - break; - case CTRL('X'): - //Move cursor to upper-left corner. - screen->setCursor(&_textPort, 0, 0); - break; - case CTRL('^'): - // CONTROL-^, X + 32, Y + 32 - //Position cursor to the X, Y coordinates. - _state = StateDCAX; - break; - - case CTRL('M'): - //screen->lineFeed(); - screen->setX(&_textPort, 0); - break; - - - case CTRL('D'): - //Delete current character (under cursor). - // TODO -- does this shift the rest of the row? Assuming yes. - screen->deletec(&_textPort); - break; - case CTRL('F'): - //Insert space at cursor. - // TODO -- does this wrap? Assuming no. - screen->insertc(&_textPort, ' '); - break; - - case CTRL('Z'): - //Delete current line. - // TODO -- textPort - screen->deleteLine(&_textPort, _textPort.cursor.y); - break; - case CTRL('V'): - //Insert blank line. - // TODO -- verify if the line is before or after the current line, - // TODO -- verify if x/y change - // TODO -- verify scrolling behavior. - // TODO -- textPort - screen->insertLine(&_textPort, _textPort.cursor.y); - break; - case CTRL('Y'): - //Clear to end of line. - screen->erase(&_textPort, Screen::EraseLineAfterCursor); - break; - case CTRL('W'): - //Clear to end of screen. - screen->erase(&_textPort, Screen::EraseAfterCursor); - break; - case CTRL('L'): - //Clear the screen (and home cursor) - screen->setCursor(&_textPort, 0, 0); - screen->erase(&_textPort, Screen::EraseAll); - break; - - - case CTRL('E'): - //Inquire if using ProTERM Special Emulation - /* - * When you send out [CONTROL-E] to a caller using ProTERM - * Special, the caller’s ProTERM will send back [CONTROL-“]”] - * (ASCII code 29). This allows a BBS to transparently - * detect the use of PSE. - */ - output->write(29); - break; - - case CTRL('R'): - //CONTROL-R, character, count - //Display character, count times. - /* - * This allows a three character code to be used to display - * multiple characters. For example, to display a window frame, - * it is necessary to show the top and bottom borders which are - * long lines of the same character (dashes, underlines, etc.). - * To draw a 64-character line consisting of equal signs, send - * [CONTROL-R = @] where “@” is the ASCII code for 64. - */ - _state = StateRepeatChar; - break; - - case CTRL('G'): - //Sound the Bell. - NSBeep(); - break; - - case CTRL('T'): - //Sound single/dual-tone for duration. - /* - * The tone command has two forms. The first invokes the single-tone - * generator, which produces relatively pure tones. The second - * invokes the dual-tone generator, which produces some rather - * interesting sounds. The three parameters, tone1, tone2, and - * duration, can all take values from 1 through 127. There is - * currently no known translation between pitch/duration values - * and actual frequencies/times. - */ - //NB - parsed but ignored, for now. - _state = StateTone1; - break; - - default: - if (c >= 0x20 && c < 0x7f) - { - screen->putc(&_textPort, c); - } - break; - - } - - return; - } - - switch (_state) - { - case StateDCAX: - _dca.x = c - 32; - _state = StateDCAY; - break; - - case StateDCAY: - _dca.y = c - 32; - screen->setCursor(&_textPort, _dca); - - _state = StateText; - break; - - case StateRepeatChar: - _repeatChar = c; - _state = StateRepeatCount; - break; - - case StateRepeatCount: - for (unsigned i = 0; i < c; ++i) - { - screen->putc(&_textPort, _repeatChar); - } - _state = StateText; - break; - - case StateTone1: - _state = StateTone2; - break; - case StateTone2: - // CONTROL-A indicates same as tone1. - _state = StateToneDuration; - break; - case StateToneDuration: - _state = StateText; - break; - } - - -} - - --(void)keyDown:(NSEvent *)event screen:(Screen *)screen output:(OutputChannel *)output -{ - NSEventModifierFlags flags = [event modifierFlags]; - NSString *chars = [event charactersIgnoringModifiers]; - - NSUInteger length = [chars length]; - - for (unsigned i = 0; i < length; ++i) - { - unichar uc = [chars characterAtIndex: i]; - - switch (uc) - { - case NSEnterCharacter: - output->write(CTRL('M')); - break; - - case NSDeleteCharacter: - output->write(0x7f); - break; - - - // backspace and left arrow use the same code, alas. - case NSBackspaceCharacter: - output->write(0x7f); - break; - - case NSLeftArrowFunctionKey: - output->write(CTRL('H')); - break; - - case NSRightArrowFunctionKey: - output->write(CTRL('U')); - break; - - case NSUpArrowFunctionKey: - output->write(CTRL('K')); - break; - - case NSDownArrowFunctionKey: - output->write(CTRL('J')); - break; - - - default: - if (uc <= 0x7f) - { - char c = uc; - if (flags & NSControlKeyMask) - c = CTRL(c); - - output->write(c); - } - break; - } - - - - } -} - -@end diff --git a/Emulators/PTSE.mm.ragel b/Emulators/PTSE.mm.ragel new file mode 100644 index 0000000..5363ee1 --- /dev/null +++ b/Emulators/PTSE.mm.ragel @@ -0,0 +1,422 @@ +// +// PTSE.mm +// 2Term +// +// Created by Kelvin Sherlock on 7/9/2010. +// Copyright 2010 __MyCompanyName__. All rights reserved. +// + +#include + + +#import "PTSE.h" + +#include "OutputChannel.h" +#include "Screen.h" + +#include "algorithm.h" + + +%%{ + machine console; + alphtype unsigned int; + + action nop {} + + action advance { + // advance cursor + if (++cursor.x == 80) { + cursor.x = 0; + if (cursor.y >= 24-1) { + screen->scrollUp(); + } else cursor.y++; + } + } + + arg1 = any ${ _scratch[0] = fc; }; + arg2 = any ${ _scratch[1] = fc; }; + + + + + main := ( + 0x00 $nop + | 0x01 ${ + // A - $01 Move cursor to beginning of line. + cursor.x = 0; + } + | 0x02 ${ + // B - $02 Move cursor to end of line. + cursor.x = 79; + } + | 0x03 $nop + | 0x04 ${ + // D - Delete current character (under cursor) + iRect tmp; + tmp.origin = cursor; + tmp.size = iSize(80 - cursor.x, 1); + screen->scrollLeft(tmp); + } + + | 0x05 ${ + /* E - $05 */ + //Inquire if using ProTERM Special Emulation + /* + * When you send out [CONTROL-E] to a caller using ProTERM + * Special, the caller’s ProTERM will send back [CONTROL-“]”] + * (ASCII code 29). This allows a BBS to transparently + * detect the use of PSE. + */ + output->write(29); + } + + | 0x06 ${ + /* F - $06 */ + // Insert space at cursor + iRect tmp; + tmp.origin = cursor; + tmp.size = iSize(80 - cursor.x, 1); + screen->scrollRight(tmp); + } + + | 0x07 ${ + /* G - $07 - beep 1000 hz for .1 seconds. */ + NSBeep(); + } + + | 0x08 ${ + /* H - $08 - Moves cursor left one column; + if cursor was at beginning of line, moves + it to end of previous line + */ + + if (cursor.x) cursor.x--; + else { + cursor.x = 80-1; + if (cursor.y) cursor.y--; + else screen->scrollDown(); + } + } + + | 0x09 ${ + // control-I + cursor.x = (cursor.x + 8) & ~7; + cursor.x = std::min(cursor.x, 72); // verified. + } + + | 0x0a ${ + /* J - $0A - Moves cursor down one row; scrolls if needed */ + + if (cursor.y >= 24-1) { + screen->scrollUp(); + } else cursor.y++; + + } + + | 0x0b ${ + /* K - $0B - Move cursor up one line */ + + if (cursor.y == 0) { + screen->scrollDown(); + } else cursor.y--; + + } + + | 0x0c ${ + // control-L + /* clear text port and home */ + // n.b - PT "home" is 0, 1 + screen->eraseScreen(); + cursor = iPoint(0,0); + } + + | 0x0d ${ + // control-M + /* carriage return */ + cursor.x = 0; + } + + | 0x0e ${ + // control-N + //Set: inverse off, mousetext off. + _context.flags = Screen::FlagNormal; + } + + | 0x0f ${ + // control-O + //Set: inverse on, mousetext off. + _context.flags = Screen::FlagInverse; + } + + | 0x10 ${ + // control-P + //Set inverse off, mousetext on. + _context.flags = Screen::FlagInverse | Screen::FlagMouseText; + } + + | 0x11 $nop + | 0x12 arg1 arg2 ${ + /* ^R */ + + /* + * This allows a three character code to be used to display + * multiple characters. For example, to display a window frame, + * it is necessary to show the top and bottom borders which are + * long lines of the same character (dashes, underlines, etc.). + * To draw a 64-character line consisting of equal signs, send + * [CONTROL-R = @] where “@” is the ASCII code for 64. + */ + + uint8_t c = _scratch[0] & 0x7f; + int count = _scratch[1]; + while (count--) { + screen->putc(c, _context); + if (cursor.x++ >= 80) { + cursor.x = 0; + if (cursor.y < 23) cursor.y++; + else screen->scrollUp(); + } + + } + + } + + | 0x13 $nop + + | 0x14 any any any ${ + //Sound single/dual-tone for duration. + // [Control-T Tl Control-A D] Sound tone Tl, for duration D + // [Control-T Tl T2 D] Sound dual tone Tl, T2 for duration D + /* + * The tone command has two forms. The first invokes the single-tone + * generator, which produces relatively pure tones. The second + * invokes the dual-tone generator, which produces some rather + * interesting sounds. The three parameters, tone1, tone2, and + * duration, can all take values from 1 through 127. There is + * currently no known translation between pitch/duration values + * and actual frequencies/times. + */ + } + + | 0x15 ${ + /* ^U - Move cursor right one character */ + } $advance + + | 0x16 ${ + /* CTRL('V') */ + // Insert blank line + iRect tmp; + tmp.origin = iPoint(0, cursor.y); + tmp.size = iSize(80, 24 - cursor.y); + screen->scrollDown(tmp); + } + + | 0x17 ${ + /* CTRL('W') */ + // Clear to end of screen + + iRect tmp; + tmp.origin = cursor; + tmp.size = iSize(80 - cursor.x, 1); + + screen->eraseRect(tmp); + + tmp = iRect(0, 0, 80, 24); + tmp.origin.y = cursor.y+1; + tmp.size.height -= cursor.y+1; + screen->eraseRect(tmp); + } + + | 0x18 ${ + // CTRL('X'): + // Home cursor (move to upper left corner) + // n.b. -- Proterm status bar on line 1. "home" is y=1. + cursor = iPoint(0,0); + + } + + | 0x19 ${ + // CTRL('Y'): + /* clear to end of line. */ + + iRect tmp; + tmp.origin = cursor; + tmp.size = iSize(80 - cursor.x, 1); + + screen->eraseRect(tmp); + } + + | 0x1a ${ + // CTRL('Z'): + /* Delete current line */ + iRect tmp; + tmp.origin = iPoint(0, cursor.y); + tmp.size = iSize(80, 24 - cursor.y); + screen->scrollUp(tmp); + + } + + | 0x1b $nop + + | 0x1c $nop + + | 0x1d $nop + + | 0x1e arg1 arg2 ${ + // CTRL('^'): + /* goto x y */ + // todo - verify behavior for illegal values. + cursor.x = clamp(_scratch[0]-32, 0, 80 - 1); + cursor.y = clamp(_scratch[1]-32, 0, 24 - 1); + } + + | 0x1f $nop + + | 0x20 .. 0x7f ${ + screen->putc(fc, _context); + } $advance + + | 0x80..0xff $nop + + )* $err{ fgoto main; }; + + write data; +}%% + + +@implementation PTSE + ++(void)load +{ + [EmulatorManager registerClass: self]; +} + ++(NSString *)name +{ + return @"Proterm Special Emulation"; +} + +-(NSString *)name +{ + return @"Proterm Special Emulation"; +} + +-(const char *)termName +{ + return "proterm-special"; +} + +-(void)reset: (Screen *)screen +{ + [self reset]; + + if (screen) screen->eraseScreen(); +} + +-(void)reset +{ + %%write init; + + _context.cursor = iPoint(0,0); + _context.window = iRect(0,0,80,24); + _context.flags = 0; +} + +-(BOOL)resizable +{ + return NO; +} + +-(struct winsize)defaultSize +{ + struct winsize ws = { 24, 80, 0, 0 }; + + return ws; +} + +-(id)init +{ + [self reset]; + return self; +} + +-(void)initTerm: (struct termios *)term +{ + // Control-U is used by the up-arrow key. + term->c_cc[VKILL] = CTRL('X'); +} + +-(void)processData:(const uint8_t *)data length: (size_t)length screen:(Screen *)screen output:(OutputChannel *)output +{ + + const uint8_t *eof = nullptr; + const uint8_t *p = data; + const uint8_t *pe = data + length; + + auto &cursor = _context.cursor; + %%write exec; + screen->setCursor(cursor); +} + +-(void)keyDown:(NSEvent *)event screen:(Screen *)screen output:(OutputChannel *)output +{ + NSEventModifierFlags flags = [event modifierFlags]; + NSString *chars = [event charactersIgnoringModifiers]; + + NSUInteger length = [chars length]; + + for (unsigned i = 0; i < length; ++i) + { + unichar uc = [chars characterAtIndex: i]; + + switch (uc) + { + case NSEnterCharacter: + output->write(CTRL('M')); + break; + + case NSDeleteCharacter: + output->write(0x7f); + break; + + + // backspace and left arrow use the same code, alas. + case NSBackspaceCharacter: + output->write(0x7f); + break; + + case NSLeftArrowFunctionKey: + output->write(CTRL('H')); + break; + + case NSRightArrowFunctionKey: + output->write(CTRL('U')); + break; + + case NSUpArrowFunctionKey: + output->write(CTRL('K')); + break; + + case NSDownArrowFunctionKey: + output->write(CTRL('J')); + break; + + + default: + if (uc <= 0x7f) + { + char c = uc; + if (flags & NSControlKeyMask) + c = CTRL(c); + + output->write(c); + } + break; + } + + + + } +} + +@end