diff --git a/2Term.xcodeproj/project.pbxproj b/2Term.xcodeproj/project.pbxproj index 093525f..bc07f8a 100644 --- a/2Term.xcodeproj/project.pbxproj +++ b/2Term.xcodeproj/project.pbxproj @@ -19,18 +19,9 @@ B612F45012DD5DAD005D1B77 /* iGeometry.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B612F44712DD5DAD005D1B77 /* iGeometry.cpp */; }; B612F45112DD5DAD005D1B77 /* Lock.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B612F44912DD5DAD005D1B77 /* Lock.cpp */; }; B612F45212DD5DAD005D1B77 /* OutputChannel.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B612F44B12DD5DAD005D1B77 /* OutputChannel.cpp */; }; - B612F45312DD5DAD005D1B77 /* Screen.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B612F44D12DD5DAD005D1B77 /* Screen.cpp */; }; - B612F46412DD5DF1005D1B77 /* Apple80.mm in Sources */ = {isa = PBXBuildFile; fileRef = B612F45712DD5DF1005D1B77 /* Apple80.mm */; }; B612F46512DD5DF1005D1B77 /* EmulatorManager.mm in Sources */ = {isa = PBXBuildFile; fileRef = B612F45912DD5DF1005D1B77 /* EmulatorManager.mm */; }; - B612F46612DD5DF1005D1B77 /* GNOConsole.mm in Sources */ = {isa = PBXBuildFile; fileRef = B612F45B12DD5DF1005D1B77 /* GNOConsole.mm */; }; - B612F46712DD5DF1005D1B77 /* PTSE.mm in Sources */ = {isa = PBXBuildFile; fileRef = B612F45D12DD5DF1005D1B77 /* PTSE.mm */; }; - B612F46812DD5DF1005D1B77 /* VT05.mm in Sources */ = {isa = PBXBuildFile; fileRef = B612F45F12DD5DF1005D1B77 /* VT05.mm */; }; - B612F46912DD5DF1005D1B77 /* VT100.mm in Sources */ = {isa = PBXBuildFile; fileRef = B612F46112DD5DF1005D1B77 /* VT100.mm */; }; - B612F46A12DD5DF1005D1B77 /* VT52.mm in Sources */ = {isa = PBXBuildFile; fileRef = B612F46312DD5DF1005D1B77 /* VT52.mm */; }; B612F47012DD5E02005D1B77 /* CurveView.m in Sources */ = {isa = PBXBuildFile; fileRef = B612F46D12DD5E02005D1B77 /* CurveView.m */; }; B612F47112DD5E02005D1B77 /* EmulatorView.mm in Sources */ = {isa = PBXBuildFile; fileRef = B612F46F12DD5E02005D1B77 /* EmulatorView.mm */; }; - B612F47412DDEBA9005D1B77 /* Screen_obsolete.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B612F47212DDEBA9005D1B77 /* Screen_obsolete.cpp */; }; - B612F47512DDEBA9005D1B77 /* Screen_TextPort.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B612F47312DDEBA9005D1B77 /* Screen_TextPort.cpp */; }; B61D0D60125B7ACA001C713B /* NewTerminalWindowController.mm in Sources */ = {isa = PBXBuildFile; fileRef = B61D0D5F125B7ACA001C713B /* NewTerminalWindowController.mm */; }; B61D0D69125B8E06001C713B /* Defaults.m in Sources */ = {isa = PBXBuildFile; fileRef = B61D0D68125B8E06001C713B /* Defaults.m */; }; B61EF7C51481561E008C1891 /* titlebar-corner.png in Resources */ = {isa = PBXBuildFile; fileRef = B61EF7C31481561E008C1891 /* titlebar-corner.png */; }; @@ -43,13 +34,16 @@ B61EF7D91482FB6D008C1891 /* titlebar-right.png in Resources */ = {isa = PBXBuildFile; fileRef = B61EF7D61482FB6D008C1891 /* titlebar-right.png */; }; B638188214A179D60027D007 /* ColorView.m in Sources */ = {isa = PBXBuildFile; fileRef = B638188114A179D60027D007 /* ColorView.m */; }; B66412391480A070003BC8D3 /* EmulatorWindow.m in Sources */ = {isa = PBXBuildFile; fileRef = B66412381480A070003BC8D3 /* EmulatorWindow.m */; }; + B675F4A81E540394004B0D9C /* Screen.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B612F44D12DD5DAD005D1B77 /* Screen.cpp */; }; + B675F4A91E561D20004B0D9C /* Apple80.mm.ragel in Sources */ = {isa = PBXBuildFile; fileRef = B612F45712DD5DF1005D1B77 /* Apple80.mm.ragel */; }; + B675F4AA1E562159004B0D9C /* GNOConsole.mm.ragel in Sources */ = {isa = PBXBuildFile; fileRef = B612F45B12DD5DF1005D1B77 /* GNOConsole.mm.ragel */; }; + B675F4AC1E56A7F2004B0D9C /* GSOSConsole.mm.ragel in Sources */ = {isa = PBXBuildFile; fileRef = B6C173911D31D2B80024E360 /* GSOSConsole.mm.ragel */; }; B676063B11DEAD3500D6B66C /* TermWindowController.mm in Sources */ = {isa = PBXBuildFile; fileRef = B676063A11DEAD3500D6B66C /* TermWindowController.mm */; }; B676065111DEBAE900D6B66C /* TermWindow.xib in Resources */ = {isa = PBXBuildFile; fileRef = B676065011DEBAE900D6B66C /* TermWindow.xib */; }; B67B3CE512B6FA040033AE07 /* a2-charset-40.png in Resources */ = {isa = PBXBuildFile; fileRef = B67B3CE312B6FA040033AE07 /* a2-charset-40.png */; }; B67B3CE612B6FA040033AE07 /* a2-charset-80.png in Resources */ = {isa = PBXBuildFile; fileRef = B67B3CE412B6FA040033AE07 /* a2-charset-80.png */; }; B6801BD912EB549300B22E9E /* vt100-charset.png in Resources */ = {isa = PBXBuildFile; fileRef = B6801BD812EB549300B22E9E /* vt100-charset.png */; }; B68E632A12FF909D00EAFF5F /* ExampleView.m in Sources */ = {isa = PBXBuildFile; fileRef = B68E632912FF909C00EAFF5F /* ExampleView.m */; }; - B6C173921D31D2B80024E360 /* GSOSConsole.mm.ragel in Sources */ = {isa = PBXBuildFile; fileRef = B6C173911D31D2B80024E360 /* GSOSConsole.mm.ragel */; }; 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 */; }; @@ -119,11 +113,11 @@ B612F44D12DD5DAD005D1B77 /* Screen.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Screen.cpp; sourceTree = ""; }; B612F44E12DD5DAD005D1B77 /* Screen.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Screen.h; sourceTree = ""; }; B612F45612DD5DF1005D1B77 /* Apple80.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Apple80.h; sourceTree = ""; }; - B612F45712DD5DF1005D1B77 /* Apple80.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = Apple80.mm; sourceTree = ""; }; + B612F45712DD5DF1005D1B77 /* Apple80.mm.ragel */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = Apple80.mm.ragel; sourceTree = ""; }; B612F45812DD5DF1005D1B77 /* Emulator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Emulator.h; sourceTree = ""; }; B612F45912DD5DF1005D1B77 /* EmulatorManager.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = EmulatorManager.mm; sourceTree = ""; }; B612F45A12DD5DF1005D1B77 /* GNOConsole.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GNOConsole.h; sourceTree = ""; }; - B612F45B12DD5DF1005D1B77 /* GNOConsole.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = GNOConsole.mm; 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 = ""; }; B612F45E12DD5DF1005D1B77 /* VT05.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = VT05.h; sourceTree = ""; }; @@ -136,8 +130,6 @@ B612F46D12DD5E02005D1B77 /* CurveView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CurveView.m; sourceTree = ""; }; B612F46E12DD5E02005D1B77 /* EmulatorView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = EmulatorView.h; sourceTree = ""; }; B612F46F12DD5E02005D1B77 /* EmulatorView.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = EmulatorView.mm; sourceTree = ""; }; - B612F47212DDEBA9005D1B77 /* Screen_obsolete.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Screen_obsolete.cpp; sourceTree = ""; }; - B612F47312DDEBA9005D1B77 /* Screen_TextPort.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Screen_TextPort.cpp; sourceTree = ""; }; B61D0D5E125B7ACA001C713B /* NewTerminalWindowController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NewTerminalWindowController.h; sourceTree = ""; }; B61D0D5F125B7ACA001C713B /* NewTerminalWindowController.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = NewTerminalWindowController.mm; sourceTree = ""; }; B61D0D67125B8E06001C713B /* Defaults.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Defaults.h; sourceTree = ""; }; @@ -296,8 +288,6 @@ B612F44C12DD5DAD005D1B77 /* OutputChannel.h */, B612F44D12DD5DAD005D1B77 /* Screen.cpp */, B612F44E12DD5DAD005D1B77 /* Screen.h */, - B612F47212DDEBA9005D1B77 /* Screen_obsolete.cpp */, - B612F47312DDEBA9005D1B77 /* Screen_TextPort.cpp */, ); path = cpp; sourceTree = ""; @@ -306,11 +296,11 @@ isa = PBXGroup; children = ( B612F45612DD5DF1005D1B77 /* Apple80.h */, - B612F45712DD5DF1005D1B77 /* Apple80.mm */, + B612F45712DD5DF1005D1B77 /* Apple80.mm.ragel */, B612F45812DD5DF1005D1B77 /* Emulator.h */, B612F45912DD5DF1005D1B77 /* EmulatorManager.mm */, B612F45A12DD5DF1005D1B77 /* GNOConsole.h */, - B612F45B12DD5DF1005D1B77 /* GNOConsole.mm */, + B612F45B12DD5DF1005D1B77 /* GNOConsole.mm.ragel */, B6C173901D31D2B80024E360 /* GSOSConsole.h */, B6C173911D31D2B80024E360 /* GSOSConsole.mm.ragel */, B612F45C12DD5DF1005D1B77 /* PTSE.h */, @@ -447,7 +437,9 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - B6C173921D31D2B80024E360 /* GSOSConsole.mm.ragel in Sources */, + B675F4A91E561D20004B0D9C /* Apple80.mm.ragel in Sources */, + B675F4AA1E562159004B0D9C /* GNOConsole.mm.ragel in Sources */, + B675F4AC1E56A7F2004B0D9C /* GSOSConsole.mm.ragel in Sources */, 8D11072D0486CEB800E47090 /* main.m in Sources */, 256AC3DA0F4B6AC300CF3369 /* TwoTermAppDelegate.mm in Sources */, B676063B11DEAD3500D6B66C /* TermWindowController.mm in Sources */, @@ -459,18 +451,10 @@ B612F45012DD5DAD005D1B77 /* iGeometry.cpp in Sources */, B612F45112DD5DAD005D1B77 /* Lock.cpp in Sources */, B612F45212DD5DAD005D1B77 /* OutputChannel.cpp in Sources */, - B612F45312DD5DAD005D1B77 /* Screen.cpp in Sources */, - B612F46412DD5DF1005D1B77 /* Apple80.mm in Sources */, B612F46512DD5DF1005D1B77 /* EmulatorManager.mm in Sources */, - B612F46612DD5DF1005D1B77 /* GNOConsole.mm in Sources */, - B612F46712DD5DF1005D1B77 /* PTSE.mm in Sources */, - B612F46812DD5DF1005D1B77 /* VT05.mm in Sources */, - B612F46912DD5DF1005D1B77 /* VT100.mm in Sources */, - B612F46A12DD5DF1005D1B77 /* VT52.mm in Sources */, B612F47012DD5E02005D1B77 /* CurveView.m in Sources */, + B675F4A81E540394004B0D9C /* Screen.cpp in Sources */, B612F47112DD5E02005D1B77 /* EmulatorView.mm in Sources */, - B612F47412DDEBA9005D1B77 /* Screen_obsolete.cpp in Sources */, - B612F47512DDEBA9005D1B77 /* Screen_TextPort.cpp in Sources */, B68E632A12FF909D00EAFF5F /* ExampleView.m in Sources */, B66412391480A070003BC8D3 /* EmulatorWindow.m in Sources */, B612870E1480B4F6002E04DF /* TermContentView.m in Sources */, @@ -563,7 +547,7 @@ isa = XCBuildConfiguration; buildSettings = { CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; - CLANG_CXX_LANGUAGE_STANDARD = "c++0x"; + CLANG_CXX_LANGUAGE_STANDARD = "c++14"; CLANG_CXX_LIBRARY = "libc++"; CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; @@ -596,7 +580,7 @@ isa = XCBuildConfiguration; buildSettings = { CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; - CLANG_CXX_LANGUAGE_STANDARD = "c++0x"; + CLANG_CXX_LANGUAGE_STANDARD = "c++14"; CLANG_CXX_LIBRARY = "libc++"; CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; diff --git a/Emulators/Apple80.h b/Emulators/Apple80.h index a1f2835..f795da7 100644 --- a/Emulators/Apple80.h +++ b/Emulators/Apple80.h @@ -10,13 +10,14 @@ #import "Emulator.h" #include "iGeometry.h" +#include "Screen.h" @interface Apple80 : NSObject { - unsigned _state; - - iPoint _dca; - + unsigned cs; + int _scratch[4]; + + context _context; } @end diff --git a/Emulators/Apple80.mm b/Emulators/Apple80.mm deleted file mode 100644 index 7d4aa6b..0000000 --- a/Emulators/Apple80.mm +++ /dev/null @@ -1,312 +0,0 @@ -// -// Apple80.mm -// 2Term -// -// Created by Kelvin Sherlock on 12/23/2010. -// Copyright 2010 __MyCompanyName__. All rights reserved. -// - -/* - * See Apple IIe Tech Ref page 273. - * See Apple IIgs Firmware Reference page 77 - * - * - */ - -#import "Apple80.h" - -#include - -#include "OutputChannel.h" -#include "Screen.h" - -@implementation Apple80 - -enum { - StateText, - - StateDCAX, - StateDCAY -}; - -+(void)load -{ - [EmulatorManager registerClass: self]; -} - -+(NSString *)name -{ - return @"Apple 80"; -} - --(NSString *)name -{ - return @"Apple 80"; -} - --(const char *)termName -{ - return "appleIIe"; -} - - --(void)reset -{ - _state = StateText; - -} - --(BOOL)resizable -{ - return NO; -} - --(struct winsize)defaultSize -{ - struct winsize ws = { 24, 80, 0, 0 }; - - return ws; -} - --(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('E'): - // cursor on - break; - - case CTRL('F'): - //cursor off - break; - - case CTRL('G'): - // beep 1000 hz for .1 seconds. - NSBeep(); - break; - - case CTRL('H'): - // decrement x. moves to end of previous line... - screen->decrementX(true); - break; - - case CTRL('I'): - // tab - screen->tabTo((screen->x() + 8) & ~0x07); - break; - - case CTRL('J'): - // down 1 line. - screen->lineFeed(); - break; - - case CTRL('K'): - // clear to end of screen - screen->erase(Screen::EraseAfterCursor); - break; - - case CTRL('L'): - // clear screen, go home. - screen->erase(Screen::EraseAll); - screen->setCursor(0, 0, true, true); - break; - - case CTRL('M'): - // move to left edge. - // IIe also did a linefeed. [?] - screen->setX(0, true); - break; - - case CTRL('N'): - // normal text. - screen->clearFlagBit(Screen::FlagInverse); - break; - - case CTRL('O'): - // inverse text. - screen->setFlagBit(Screen::FlagInverse); - break; - - case CTRL('Q'): - // 40 column mode. - break; - - case CTRL('R'): - // 80 column mode - break; - - case CTRL('S'): - // stop listing until another key pressed. - break; - - case CTRL('U'): - // deactivate 80 column firmware - break; - - case CTRL('V'): - // scroll down 1 line, leaving cursor at current position. - screen->deleteLine(0); - break; - - case CTRL('W'): - // scroll up 1 line, leaving cursor at current position. - screen->insertLine(0); - break; - - case CTRL('X'): - //mouse text off - screen->clearFlagBit(Screen::FlagMouseText); - break; - - case CTRL('Y'): - // cursor home - screen->setCursor(0, 0, true, true); - break; - - case CTRL('Z'): - // clear entire line - screen->erase(Screen::EraseLineAll); - break; - - case CTRL('['): - // mouse text on - screen->setFlagBit(Screen::FlagMouseText); - break; - - - case CTRL('\\'): - // move cursor 1 character to the right - // TODO -- should wrap to next line. - screen->incrementX(true); - break; - - case CTRL(']'): - // clear to end of line. - // TODO -- should also clear cursor. - screen->erase(Screen::EraseLineAfterCursor); - break; - - case CTRL('^'): - // goto x y - _state = StateDCAX; - break; - - case CTRL('_'): - // move up 1 line, no scroll. - screen->decrementY(true); - break; - - default: - if (c >= 0x20 && c < 0x7f) - { - screen->putc(c); - } - break; - - } - - return; - } - - switch (_state) - { - case StateDCAX: - _dca.x = c - 32; - _state = StateDCAY; - break; - - case StateDCAY: - _dca.y = c - 32; - screen->setCursor(_dca); - - _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; - */ - - - // the Apple II keyboard had a delete where the backspace key was. - // it functions as a backspace key. - 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; - - //NSLog(@"%@", event); - - if (flags & NSAlphaShiftKeyMask) - { - c = flags & NSShiftKeyMask ? tolower(c) : toupper(c); - } - - if (flags & NSControlKeyMask) - c = CTRL(c); - - output->write(c); - } - break; - } - - - - } -} - -@end diff --git a/Emulators/Apple80.mm.ragel b/Emulators/Apple80.mm.ragel new file mode 100644 index 0000000..8591803 --- /dev/null +++ b/Emulators/Apple80.mm.ragel @@ -0,0 +1,396 @@ +// +// Apple80.mm +// 2Term +// +// Created by Kelvin Sherlock on 12/23/2010. +// Copyright 2010 __MyCompanyName__. All rights reserved. +// + +/* + * See Apple IIe Tech Ref page 273. + * See Apple IIgs Firmware Reference page 77 + * See Apple IIe Extended 80-Column TextCard (revision B) appendix C. + * + */ + +#import "Apple80.h" + +#include + +#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 - 32); }; + arg2 = any ${ _scratch[1] = (fc - 32); }; + + + + + main := ( + 0x00 $nop + | 0x01 $nop + | 0x02 $nop + | 0x03 $nop + | 0x04 $nop + + | 0x05 ${ + /* E - $05 - Turns cursor on (enables cursor display) */ + screen->setCursorType(Screen::CursorTypeUnderscore); + } + + | 0x06 ${ + /* F - $06 - Turns cursor off (disables cursor display) */ + screen->setCursorType(Screen::CursorTypeNone); + } + + | 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; + // go up, possibly scrolling. + if (cursor.y) cursor.y--; + } + } + + | 0x09 ${ + // control-I + // tab -- todo, verify. + cursor.x = (cursor.x + 8) & ~7; + cursor.x = std::min(cursor.x, 79); + } + + | 0x0a ${ + /* J - $0A - Moves cursor down one row; scrolls if needed */ + + if (cursor.y >= 24-1) { + screen->scrollUp(); + } else cursor.y++; + + } + + | 0x0b ${ + /* K - $0B - Clears 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); + + } + + | 0x0c ${ + // control-L + /* clear text port and home */ + screen->eraseScreen(); + cursor = iPoint(0,0); + } + + | 0x0d ${ + // control-M + /* carriage return */ + // BASIC also moves to next line, PASCAL does not. + cursor.x = 0; + } + + | 0x0e ${ + // control-N + /* set normal display */ + _context.clearFlagBit(Screen::FlagInverse); + } + + | 0x0f ${ + // control-O + /* set inverse display */ + _context.setFlagBit(Screen::FlagInverse); + } + + | 0x10 $nop + + | 0x11 ${ /* ^Q 40 column mode */ } + | 0x12 ${ /* ^R 80 column mode */ } + + | 0x13 ${ /* ^S - stop listing until any keypress. */ } + + | 0x14 $nop + + | 0x15 ${ + /* ^U - deactivate 80 column? */ + } + + | 0x16 ${ /* CTRL('V') */ screen->scrollDown(); } + + | 0x17 ${ /* CTRL('W') */ screen->scrollUp(); } + + | 0x18 ${ + // CTRL('X'): + /* disable mouse text */ + _context.clearFlagBit(Screen::FlagMouseText); + } + + | 0x19 ${ + // CTRL('Y'): + /* home cursor */ + cursor = iPoint(0,0); + } + + | 0x1a ${ + // CTRL('Z'): + /* clear line */ + iRect tmp; + tmp.origin = iPoint(0, cursor.y); + tmp.size = iSize(80, 1); + screen->eraseRect(tmp); + } + + | 0x1b ${ + // CTRL('['): + /* ^[ enable mouse text mapping */ + _context.setFlagBit(Screen::FlagMouseText); + } + + | 0x1c ${ + // CTRL('\\'): + /* Moves cursor right one column; if at end of line, does Control-M */ + // n.b. - BASIC ^M also moves to next line. + cursor.x++; + if (cursor.x == 80) cursor.x = 0; + } + + | 0x1d ${ + // CTRL(']'): + /* clear to end of line */ + iRect tmp; + tmp.origin = cursor; + tmp.size = iSize(80 - cursor.x, 1); + + screen->eraseRect(tmp); + } + + | 0x1e arg1 arg2 ${ + // CTRL('^'): + /* goto x y */ + // todo - verify behavior for illegal values. + cursor.x = clamp(_scratch[0], 0, 80 - 1); + cursor.y = clamp(_scratch[1], 0, 24 - 1); + } + + | 0x1f ${ + // CTRL('_'): + /* move cursor up */ + if (cursor.y) cursor.y--; + } + + | 0x20 .. 0x7f ${ + screen->putc(fc, _context); + } $advance + + | 0x80 .. 0x9f ${ + /* uppercase inverse/normal */ + uint8_t flag = ~(_context.flags & Screen::FlagInverse); + screen->putc(fc - 0x40, _context.cursor, flag); + } $advance + + | 0xa0 .. 0xbf ${ + /* special inverse/normal */ + uint8_t flag = ~(_context.flags & Screen::FlagInverse); + screen->putc(fc - 0x80, _context.cursor, flag); + } $advance + + | 0xc0 .. 0xdf ${ + /* uppercase normal / mouse text. */ + uint8_t flag = ~(_context.flags & Screen::FlagInverse); + if (flag) flag |= Screen::FlagMouseText; + screen->putc(fc - 0x80, _context.cursor, flag); + } $advance + + | 0xe0 .. 0xff ${ + /* special inverse/normal */ + uint8_t flag = ~(_context.flags & Screen::FlagInverse); + screen->putc(fc - 0x80, _context.cursor, flag); + } $advance + + )* $err{ fgoto main; }; + + write data; +}%% + +@implementation Apple80 + + ++(void)load +{ + [EmulatorManager registerClass: self]; +} + ++(NSString *)name +{ + return @"Apple 80"; +} + +-(NSString *)name +{ + return @"Apple 80"; +} + +-(const char *)termName +{ + return "appleIIe"; +} + + +-(void)reset +{ + + %%write init; + _context.window = iRect(0, 0, 80, 24); + _context.cursor = iPoint(0,0); + _context.flags = 0; +} + +-(BOOL)resizable +{ + return NO; +} + +-(struct winsize)defaultSize +{ + struct winsize ws = { 24, 80, 0, 0 }; + + return ws; +} + +-(void)initTerm: (struct termios *)term +{ + // Control-U is used by the up-arrow key. + term->c_cc[VKILL] = CTRL('X'); +} + +-(id)init +{ + if ((self = [super init])) + { + [self reset]; + } + + return self; +} + +-(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; + */ + + + // the Apple II keyboard had a delete where the backspace key was. + // it functions as a backspace key. + 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; + + //NSLog(@"%@", event); + + if (flags & NSAlphaShiftKeyMask) + { + c = flags & NSShiftKeyMask ? tolower(c) : toupper(c); + } + + if (flags & NSControlKeyMask) + c = CTRL(c); + + output->write(c); + } + break; + } + + + + } +} + +@end diff --git a/Emulators/GNOConsole.h b/Emulators/GNOConsole.h index 6dc78b6..a8cac06 100644 --- a/Emulators/GNOConsole.h +++ b/Emulators/GNOConsole.h @@ -15,17 +15,11 @@ @interface GNOConsole : NSObject { - unsigned _state; - - TextPort _textPort; - - - iPoint _dca; - - int _vp[4]; - + unsigned cs; + context _context; Screen::CursorType _cursorType; + int _scratch[4]; } diff --git a/Emulators/GNOConsole.mm b/Emulators/GNOConsole.mm deleted file mode 100644 index 36c90d2..0000000 --- a/Emulators/GNOConsole.mm +++ /dev/null @@ -1,497 +0,0 @@ -// -// GNOConsole.mm -// 2Term -// -// Created by Kelvin Sherlock on 7/9/2010. -// Copyright 2010 __MyCompanyName__. All rights reserved. -// - -#include - - -#import "GNOConsole.h" - -#include "OutputChannel.h" -#include "Screen.h" - - -/* - * The GNO Console Driver. - * this was gleaned from the source code. - * - * 0x00 n/a - * 0x01 ^A - enable overstrike mode (IODP_gInsertFlag = 0) - * 0x02 ^B - enable insert mode (IODP_gInsertFlag = 1) - * 0x03 ^C - setport (IODP_GotoFlag = 3) - * 0x04 n/a - * 0x05 ^E - turn on cursor - * 0x06 ^F - turn off cursor - * 0x07 ^G - beep - * 0x08 ^H - left arrow - * 0x09 ^I - tab - * 0x0a ^J - Line Feed (checks IODP_Scroll) - * 0x0b ^K - clear EOP ???? up arrow??? - * 0x0c ^L - form feed - clear screen - * 0x0d ^M - carriage return cursor = left margin - * 0x0e ^N - inverse off - invert flag &= 0x7fff - * 0x0f ^O - inverse on - invert flag |= 0x8000 - * 0x10 n/a - * 0x11 ^Q - insert line - * 0x12 ^R - Delete Line - * 0x13 n/a - * 0x14 n/a - * 0x15 ^U - right arrow - * 0x16 ^V - scroll down 1 line - * 0x17 ^W - scroll up 1 line - * 0x18 ^X - mouse text off. - * 0x19 ^Y - cursor home. - * 0x1a ^Z - clear line - * 0x1b ^[ - mouse text on (inv flag | 0x4000) - * 0x1c ^\ - increment IODP_CH (kill?) - * 0x1d ^] -clear EOL - * 0x1e ^^ - goto xy (IODP_GotoFlag = 1) - * 0x1f ^_ - up arrow - * - * mouse text only applies if mouse text and inverse are on. - - - * set port - 0x03 '[' left-margin right-margin top-margin bottom-margin [any printable character] - */ - - -/* - this was gleaned from the kernel reference manual. - - The new console driver supports all the features of the old 80-column Pascal firmware, - and adds a few extensions, with one exception - the codes that switched between 40 and - 80 columns modes are not supported. It is not compatible with the GS/OS '.console' - driver. The control codes supported are as follows: - - Hex ASCII Action - 01 CTRL-A set cursor to flashing block - 02 CTRL-B set cursor to flashing underscore - 03 CTRL-C Begin "Set Text Window" sequence - 05 CTRL-E Cursor on - 06 CTRL-F Cursor off - 07 CTRL-G Perform FlexBeep - 08 CTRL-H Move left one character - 09 CTRL-I Tab - 0A CTRL-J Move down a line - 0B CTRL-K Clear to EOP (end of screen) - 0C CTRL-L Clear screen, home cursor - 0D CTRL-M Move cursor to left edge of line - 0E CTRL-N Normal text - 0F CTRL-O Inverse text - 11 CTRL-Q Insert a blank line at the current cursor position - 12 CTRL-R Delete the line at the current cursor position. - 15 CTRL-U Move cursor right one character - 16 CTRL-V Scroll display down one line - 17 CTRL-W Scroll display up one line - 18 CTRL-X Normal text, mousetext off - 19 CTRL-Y Home cursor - 1A CTRL-Z Clear entire line - 1B CTRL-[ MouseText on - 1C CTRL-\ Move cursor one character to the right - 1D CTRL-] Clear to end of line - 1E CTRL-^ Goto XY - 1F CTRL-_ Move up one line - - (Note: the Apple IIgs Firmware Reference incorrectly has codes 05 and 06 reversed. The - codes listed here are correct for both GNO/ME and the Apple IIgs 80-column firmware) - - The Set Text Window sequence (begun by a $03 code) works as follows: - - CTRL-C '[' LEFT RIGHT TOP BOTTOM - - CTRL-C is of course hex $03, and '[' is the open bracket character ($5B). TOP, BOTTOM, - LEFT, and RIGHT are single-byte ASCII values that represent the margin settings. Values - for TOP and BOTTOM range from 0 to 23; LEFT and RIGHT range from 0 to 79. TOP must be - numerically less than BOTTOM; LEFT must be less than RIGHT. Any impossible settings are - ignored, and defaults are used instead. The extra '[' in the sequence helps prevent the - screen from becoming confused in the event that random data is printed to the screen. - - After a successful Set Text Window sequence, only the portion of the screen inside the - 'window' will be accessible, and only the window will scroll; any text outside the - window is not affected. - - */ - -@implementation GNOConsole - -enum { - StateText, - - StateDCAX, - StateDCAY, - - StateSetPort1, - StateSetPort2, - StateSetPort3, - StateSetPort4, - StateSetPort5 -}; - -+(void)load -{ - [EmulatorManager registerClass: self]; -} - -+(NSString *)name -{ - return @"GNO Console"; -} - --(NSString *)name -{ - return @"GNO Console"; -} - --(const char *)termName -{ - return "gno-console"; -} - - --(void)reset -{ - _state = StateText; - - _textPort.frame = iRect(0, 0, 80, 24); - _textPort.cursor = iPoint(0,0); - - _textPort.scroll = true; - _textPort.advanceCursor = true; - _textPort.leftMargin = TextPort::MarginWrap; - _textPort.rightMargin = TextPort::MarginWrap; - - _cursorType = Screen::CursorTypeUnderscore; - - // set flags to plain text. -} - --(BOOL)resizable -{ - return NO; -} - --(struct winsize)defaultSize -{ - struct winsize ws = { 24, 80, 0, 0 }; - - return ws; -} - --(void)initTerm: (struct termios *)term -{ - // Control-U is used by the up-arrow key. - term->c_cc[VKILL] = CTRL('X'); -} - --(id)init -{ - if ((self = [super init])) - { - [self reset]; - } - - return self; -} - --(void)processCharacter:(uint8_t)c screen:(Screen *)screen output:(OutputChannel *)output -{ - - if (_state == StateText) - { - switch (c) - { - case CTRL('A'): - // set cursor to flashing block. - _cursorType = Screen::CursorTypeBlock; - screen->setCursorType(_cursorType); - break; - case CTRL('B'): - _cursorType = Screen::CursorTypeUnderscore; - screen->setCursorType((Screen::CursorType)_cursorType); - // set cursor to flashing underscore. - break; - - case CTRL('C'): - // begin set text window sequence - _state = StateSetPort1; - break; - - case CTRL('E'): - // cursor on - screen->setCursorType(_cursorType); - break; - - case CTRL('F'): - //cursor off - screen->setCursorType(Screen::CursorTypeNone); - break; - - case CTRL('G'): - NSBeep(); - break; - - case CTRL('H'): - screen->decrementX(&_textPort); - //screen->decrementX(true); - break; - - case CTRL('I'): - // tab - screen->tabTo(&_textPort, (_textPort.cursor.x + 8) & ~0x07); - //screen->tabTo((screen->x() + 8) & ~0x07); - break; - - case CTRL('J'): - // down 1 line. - screen->lineFeed(&_textPort); - break; - - case CTRL('K'): - // clear to end of screen - screen->erase(&_textPort, Screen::EraseAfterCursor); - break; - - case CTRL('L'): - // clear screen, go home. - screen->erase(&_textPort, Screen::EraseAll); - screen->setCursor(&_textPort, 0, 0); - break; - - case CTRL('M'): - // move to left edge. - screen->setX(&_textPort, 0); - break; - - case CTRL('N'): - // normal text. - screen->clearFlagBit(Screen::FlagInverse); - break; - - case CTRL('O'): - // inverse text. - screen->setFlagBit(Screen::FlagInverse); - break; - - case CTRL('Q'): - // insert line. - // TODO -- verify textPort - screen->insertLine(&_textPort, _textPort.cursor.y); - break; - - case CTRL('R'): - // delete line - // TODO -- verify textPort - screen->deleteLine(&_textPort, _textPort.cursor.y); - break; - - case CTRL('U'): - // right arrow. - screen->incrementX(&_textPort); - break; - - case CTRL('V'): - // scroll down 1 line. - screen->insertLine(&_textPort, 0); - break; - case CTRL('W'): - // scroll up 1 line. - screen->deleteLine(&_textPort, 0); - break; - - case CTRL('X'): - //mouse text off - screen->clearFlagBit(Screen::FlagMouseText); - break; - - case CTRL('Y'): - // cursor home - screen->setCursor(&_textPort, 0, 0); - break; - - case CTRL('Z'): - // clear entire line - screen->erase(&_textPort, Screen::EraseLineAll); - break; - - case CTRL('['): - // mouse text on - // inverse must also be on. - screen->setFlagBit(Screen::FlagMouseText); - break; - - case CTRL('\\'): - // move cursor 1 character to the right - screen->incrementX(&_textPort); - break; - - case CTRL(']'): - // clear to end of line. - screen->erase(&_textPort, Screen::EraseLineAfterCursor); - break; - - case CTRL('^'): - // goto x y - _state = StateDCAX; - break; - - case CTRL('_'): - // move up 1 line - screen->decrementY(&_textPort); - break; - - default: - if (c >= 0x20 && c < 0x7f) - { - screen->putc(&_textPort, c); - } - break; - - } - - return; - } - - switch (_state) - { - case StateDCAX: - _dca.x = (c & 0x7f) - 32; - _state = StateDCAY; - break; - - case StateDCAY: - _dca.y = (c & 0x7f) - 32; - // goto xy does not respect the text window. - - if (_dca.x >= 80) _dca.x = 0; - if (_dca.y >= 24) _dca.y = 0; - screen->setCursor(&_textPort, _dca); - //screen->setCursor(_dca); - _state = StateText; - break; - - case StateSetPort1: - // [ - if (c == '[') - _state++; - else - _state = StateText; - break; - - case StateSetPort2: - // left - _vp[0] = (c & 0x7f) - 32; - _state++; - break; - - case StateSetPort3: - // right - _vp[1] = (c & 0x7f) - 32 + 1; - _state++; - break; - - case StateSetPort4: - // top - _vp[2] = (c & 0x7f) - 32; - _state++; - break; - case StateSetPort5: - // bottom - // and validation. - - _vp[3] = (c & 0x7f) - 32 + 1; - - _vp[0] = std::max(0, _vp[0]); - _vp[2] = std::max(0, _vp[2]); - - - _vp[1] = std::min(80, _vp[1]); - _vp[3] = std::min(24, _vp[3]); - - - if (_vp[1] <= _vp[0]) _vp[1] = 80; - if (_vp[3] <= _vp[2]) _vp[3] = 24; - - - _textPort.frame = iRect(_vp[0], _vp[2], _vp[1] - _vp[0], _vp[3] - _vp[2]); - - - - // move the cursor to the top left - // gnome clamps the horizontal, doesn't adjust the vertical. - screen->setCursor(&_textPort, iPoint(0,0)); - - - _state = StateText; - - } - - -} - - --(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; - */ - - 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 & (NSShiftKeyMask | NSAlphaShiftKeyMask)) - { - c = toupper(c); - } - if (flags & NSControlKeyMask) - c = CTRL(c); - - output->write(c); - } - break; - } - } -} - -@end diff --git a/Emulators/GNOConsole.mm.ragel b/Emulators/GNOConsole.mm.ragel new file mode 100644 index 0000000..ecc2a09 --- /dev/null +++ b/Emulators/GNOConsole.mm.ragel @@ -0,0 +1,536 @@ +// +// GNOConsole.mm +// 2Term +// +// Created by Kelvin Sherlock on 7/9/2010. +// Copyright 2010 __MyCompanyName__. All rights reserved. +// + +#include + + +#import "GNOConsole.h" + +#include "OutputChannel.h" +#include "Screen.h" + + +/* + * The GNO Console Driver. + * this was gleaned from the source code. + * + * 0x00 n/a + * 0x01 ^A - enable overstrike mode (IODP_gInsertFlag = 0) + * 0x02 ^B - enable insert mode (IODP_gInsertFlag = 1) + * 0x03 ^C - setport (IODP_GotoFlag = 3) + * 0x04 n/a + * 0x05 ^E - turn on cursor + * 0x06 ^F - turn off cursor + * 0x07 ^G - beep + * 0x08 ^H - left arrow + * 0x09 ^I - tab + * 0x0a ^J - Line Feed (checks IODP_Scroll) + * 0x0b ^K - clear EOP ???? up arrow??? + * 0x0c ^L - form feed - clear screen + * 0x0d ^M - carriage return cursor = left margin + * 0x0e ^N - inverse off - invert flag &= 0x7fff + * 0x0f ^O - inverse on - invert flag |= 0x8000 + * 0x10 n/a + * 0x11 ^Q - insert line + * 0x12 ^R - Delete Line + * 0x13 n/a + * 0x14 n/a + * 0x15 ^U - right arrow + * 0x16 ^V - scroll down 1 line + * 0x17 ^W - scroll up 1 line + * 0x18 ^X - mouse text off. + * 0x19 ^Y - cursor home. + * 0x1a ^Z - clear line + * 0x1b ^[ - mouse text on (inv flag | 0x4000) + * 0x1c ^\ - increment IODP_CH (kill?) + * 0x1d ^] -clear EOL + * 0x1e ^^ - goto xy (IODP_GotoFlag = 1) + * 0x1f ^_ - up arrow + * + * mouse text only applies if mouse text and inverse are on. + + + * set port - 0x03 '[' left-margin right-margin top-margin bottom-margin [any printable character] + */ + + +/* + this was gleaned from the kernel reference manual. + + The new console driver supports all the features of the old 80-column Pascal firmware, + and adds a few extensions, with one exception - the codes that switched between 40 and + 80 columns modes are not supported. It is not compatible with the GS/OS '.console' + driver. The control codes supported are as follows: + + Hex ASCII Action + 01 CTRL-A set cursor to flashing block + 02 CTRL-B set cursor to flashing underscore + 03 CTRL-C Begin "Set Text Window" sequence + 05 CTRL-E Cursor on + 06 CTRL-F Cursor off + 07 CTRL-G Perform FlexBeep + 08 CTRL-H Move left one character + 09 CTRL-I Tab + 0A CTRL-J Move down a line + 0B CTRL-K Clear to EOP (end of screen) + 0C CTRL-L Clear screen, home cursor + 0D CTRL-M Move cursor to left edge of line + 0E CTRL-N Normal text + 0F CTRL-O Inverse text + 11 CTRL-Q Insert a blank line at the current cursor position + 12 CTRL-R Delete the line at the current cursor position. + 15 CTRL-U Move cursor right one character + 16 CTRL-V Scroll display down one line + 17 CTRL-W Scroll display up one line + 18 CTRL-X Normal text, mousetext off + 19 CTRL-Y Home cursor + 1A CTRL-Z Clear entire line + 1B CTRL-[ MouseText on + 1C CTRL-\ Move cursor one character to the right + 1D CTRL-] Clear to end of line + 1E CTRL-^ Goto XY + 1F CTRL-_ Move up one line + + (Note: the Apple IIgs Firmware Reference incorrectly has codes 05 and 06 reversed. The + codes listed here are correct for both GNO/ME and the Apple IIgs 80-column firmware) + + The Set Text Window sequence (begun by a $03 code) works as follows: + + CTRL-C '[' LEFT RIGHT TOP BOTTOM + + CTRL-C is of course hex $03, and '[' is the open bracket character ($5B). TOP, BOTTOM, + LEFT, and RIGHT are single-byte ASCII values that represent the margin settings. Values + for TOP and BOTTOM range from 0 to 23; LEFT and RIGHT range from 0 to 79. TOP must be + numerically less than BOTTOM; LEFT must be less than RIGHT. Any impossible settings are + ignored, and defaults are used instead. The extra '[' in the sequence helps prevent the + screen from becoming confused in the event that random data is printed to the screen. + + After a successful Set Text Window sequence, only the portion of the screen inside the + 'window' will be accessible, and only the window will scroll; any text outside the + window is not affected. + + */ + +%%{ + machine console; + alphtype unsigned int; + + action nop {} + + action forward { + if (cursor.x > window.maxX()-1) { + cursor.x = window.minX(); + if (cursor.y >= window.maxY()-1) { + screen->scrollUp(window); + } else cursor.y++; + } + + } + + arg1 = any ${ _scratch[0] = ((fc & 0x7f) - 32); }; + arg2 = any ${ _scratch[1] = ((fc & 0x7f) - 32); }; + arg3 = any ${ _scratch[2] = ((fc & 0x7f) - 32); }; + arg4 = any ${ _scratch[3] = ((fc & 0x7f) - 32); }; + + main := ( + 0x00 $nop + | 0x01 ${ + // CTRL(A) + // set cursor to flashing block. + _cursorType = Screen::CursorTypeBlock; + screen->setCursorType(_cursorType); + } + + | 0x02 ${ + // CTRL('B') + // set cursor to flashing underscore. + _cursorType = Screen::CursorTypeUnderscore; + screen->setCursorType((Screen::CursorType)_cursorType); + } + + | 0x03 '[' arg1 arg2 arg3 arg4 ${ + // CTRL('C'): + // '[' left right top bottom + // n.b. - 0, 79, 0, 23 is full screen. + + + _scratch[0] = std::max(0, _scratch[0]); + _scratch[2] = std::max(0, _scratch[2]); + + + _scratch[1] = std::min(80-1, _scratch[1])+1; + _scratch[3] = std::min(24-1, _scratch[3])+1; + + + if (_scratch[1] <= _scratch[0]) _scratch[1] = 80; + if (_scratch[3] <= _scratch[2]) _scratch[3] = 24; + + + window = iRect( + iPoint(_scratch[0], _scratch[2]), + iPoint(_scratch[1], _scratch[3]) + ); + + + // move the cursor to the top left + // gnome clamps the horizontal, doesn't adjust the vertical. + //screen->setCursor(&_textPort, iPoint(0,0)); + + if (cursor.x < _scratch[0]) cursor.x = _scratch[0]; + if (cursor.x >= _scratch[1]) cursor.x = _scratch[1] - 1; + } + + | 0x04 $nop + + | 0x05 ${ + // CTRL('E'): + // cursor on + screen->setCursorType(_cursorType); + } + + | 0x06 ${ + //CTRL('F'): + //cursor off + screen->setCursorType(Screen::CursorTypeNone); + } + + | 0x07 ${ + //CTRL('G'): + NSBeep(); + } + + | 0x08 ${ + // CTRL('H'): + + if (cursor.x == window.minX()) { + cursor.x = window.maxX()-1; + // go up, possibly scrolling. + if (cursor.y != window.minY()) cursor.y--; + } + else cursor.x--; + + } + + | 0x09 ${ + // CTRL('I'): + // tab + cursor.x = (cursor.x + 8) & ~ 0x07; + } $forward + + | 0x0a ${ + // CTRL('J'): + // down 1 line. + if (cursor.y >= window.maxY()-1) { + screen->scrollUp(window); + } else cursor.y++; + } + + | 0x0b ${ + // CTRL('K'): + // clear to end of screen + + + iRect tmp; + tmp.origin = cursor; + tmp.size = iSize(window.size.width - cursor.x, 1); + + screen->eraseRect(tmp); + + tmp = window; + tmp.origin.y = cursor.y+1; + tmp.size.height -= cursor.y+1; + screen->eraseRect(tmp); + } + + | 0x0c ${ + // CTRL('L'): + // clear screen, go home. + screen->eraseRect(window); + cursor = window.origin; + } + + | 0x0d ${ + // CTRL('M'): + // move to left edge. + cursor.x = window.minX(); + } + + | 0x0e ${ + // CTRL('N'): + // normal text. + _context.clearFlagBit(Screen::FlagInverse); + } + + | 0x0f ${ + // CTRL('O'): + // inverse text. + _context.setFlagBit(Screen::FlagInverse); + } + + | 0x10 $nop + + | 0x11 ${ + // CTRL('Q'): + // insert line. + iRect tmp(iPoint(window.minX(), cursor.y), window.bottomRight()); + screen->scrollDown(tmp); + } + + | 0x12 ${ + // CTRL('R'): + // delete line + iRect tmp(iPoint(window.minX(), cursor.y), window.bottomRight()); + screen->scrollUp(tmp); + + } + + | 0x13 $nop + | 0x14 $nop + + | 0x15 $forward ${ + // CTRL('U'): + // right arrow. + cursor.x++; + } $forward + + | 0x16 ${ + // CTRL('V'): + // scroll down 1 line. + screen->scrollDown(window); + } + + | 0x17 ${ + // CTRL('W'): + // scroll up 1 line. + screen->scrollUp(window); + } + + | 0x18 ${ + // CTRL('X'): + //mouse text off + _context.clearFlagBit(Screen::FlagMouseText); + } + + | 0x19 ${ + // CTRL('Y'): + // cursor home + cursor.x = 0; + cursor.y = 0; + } + + | 0x1a ${ + // CTRL('Z'): + // clear entire line + + iRect tmp; + tmp.origin = iPoint(window.origin.x, cursor.y); + tmp.size = iSize(window.size.width, 1); + screen->eraseRect(tmp); + } + + | 0x1b ${ + // CTRL('['): + // mouse text on + // inverse must also be on. + _context.setFlagBit(Screen::FlagMouseText); + } + + | 0x1c ${ + // CTRL('\\'): + // move cursor 1 character to the right + cursor.x++; + } + + | 0x1d ${ + // CTRL(']'): + // clear to end of line. + + iRect tmp; + tmp.origin = cursor; + tmp.size = iSize(window.size.width - cursor.x, 1); + + screen->eraseRect(tmp); + } + + | 0x1e arg1 arg2 ${ + // CTRL('^'): + // goto x y + // goto xy does not respect the text window. + if (_scratch[0] >= 80) _scratch[0] = 0; + if (_scratch[1] >= 24) _scratch[1] = 0; + cursor = iPoint(_scratch[0], _scratch[1]); + } + + | 0x1f ${ + // CTRL('_'): + // move up 1 line + if (cursor.y != window.minY()) cursor.y--; + } + + + + | 0x20 .. 0x7f $forward ${ + screen->putc(fc, _context); + cursor.x++; + } + | 0x80 .. 0xff $nop + )* $err{ fgoto main; }; + + write data; +}%% + + +@implementation GNOConsole + + + + + ++(void)load +{ + [EmulatorManager registerClass: self]; +} + ++(NSString *)name +{ + return @"GNO Console"; +} + +-(NSString *)name +{ + return @"GNO Console"; +} + +-(const char *)termName +{ + return "gno-console"; +} + + +-(void)reset +{ + + %%write init; + + _context.flags = 0; + _context.window = iRect(0, 0, 80, 24); + _context.cursor = iPoint(0,0); + + + _cursorType = Screen::CursorTypeUnderscore; + + // set flags to plain text. +} + +-(BOOL)resizable +{ + return NO; +} + +-(struct winsize)defaultSize +{ + struct winsize ws = { 24, 80, 0, 0 }; + + return ws; +} + +-(void)initTerm: (struct termios *)term +{ + // Control-U is used by the up-arrow key. + term->c_cc[VKILL] = CTRL('X'); +} + +-(id)init +{ + if ((self = [super init])) + { + [self reset]; + } + + return self; +} + +-(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; + + iPoint &cursor = _context.cursor; + iRect &window = _context.window; + + %%write exec; + + if (cursor.x == 80) screen->setCursor(iPoint(79, cursor.y)); + else 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; + */ + + 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 & (NSShiftKeyMask | NSAlphaShiftKeyMask)) + { + c = toupper(c); + } + if (flags & NSControlKeyMask) + c = CTRL(c); + + output->write(c); + } + break; + } + } +} + +@end diff --git a/Emulators/GSOSConsole.h b/Emulators/GSOSConsole.h index 36671eb..b398c29 100644 --- a/Emulators/GSOSConsole.h +++ b/Emulators/GSOSConsole.h @@ -12,18 +12,26 @@ #include "iGeometry.h" #include "Screen.h" +struct gsos_context : public context { + bool consWrap = true; + bool consAdvance = true; + bool consLF = true; + bool consScroll = true; + bool consVideo = true; + bool consDLE = true; + bool consMouse = false; + uint8_t consFill = 0xa0; +}; + @interface GSOSConsole : NSObject { + + + gsos_context _context; + std::vector _context_stack; + unsigned cs; - - TextPort _textPort; - - std::vector _tpStack; - int _scratch[4]; - int _cursorType; - bool _consLF; - bool _consDLE; } @end diff --git a/Emulators/GSOSConsole.mm.ragel b/Emulators/GSOSConsole.mm.ragel index aca9c81..bae4be0 100644 --- a/Emulators/GSOSConsole.mm.ragel +++ b/Emulators/GSOSConsole.mm.ragel @@ -13,6 +13,22 @@ #include "algorithm.h" +// currently no way to set consFill :-) +static char_info remap_fill(const gsos_context &ctx) { return char_info(' ', 0); } + +static void advance(Screen *screen, gsos_context &ctx) { + + if (ctx.consAdvance) { + if (ctx.cursor.x < ctx.window.maxX() - 1) ctx.cursor.x++; + else if (ctx.consWrap) { + ctx.cursor.x = ctx.window.minX(); + if (ctx.cursor.y < ctx.window.maxY() - 1) ctx.cursor.y++; + else if (ctx.consScroll) screen->scrollUp(ctx.window); + } + } + +} + %%{ machine console; alphtype unsigned int; @@ -24,21 +40,32 @@ arg3 = any ${ _scratch[2] = (fc - 32); }; arg4 = any ${ _scratch[3] = (fc - 32); }; + + action advance_if { + if (_context.consAdvance) advance(screen, _context); + } + main := ( 0x00 $nop | 0x01 ${ - /* save current textport and reset text port to default. */ + /* + Save Current Text Port and Reset Default Text Port + + Saves the current text port and resets to the default text port. If the system is out of memory, no error is returned, and the text port is simply reset. + */ - _tpStack.push_back(_textPort); - // todo -- consLF, consDLE? - _textPort = TextPort(); - + _context_stack.push_back(_context); + _context = gsos_context(); + _context.cursor = iPoint(0,0); + _context.window = iRect(0, 0, 80, 24); } | 0x02 arg1 arg2 arg3 arg4 ${ - /* set the current text port */ + /* + Set Text Port Size - // left, top, right, bottom + Accepts the next four bytes as absolute screen coordinates + 32. Sets the current text port to the new parameters. The parameters are in the following order: windLeft, windTop, windRight, windBottom. Any parameter outside the screen boundaries is clipped to a legal value. The cursor is set to the upper-left corner of the new text port. + */ _scratch[0] = clamp(_scratch[0], 0, 80-1); _scratch[1] = clamp(_scratch[1], 0, 24-1); @@ -47,101 +74,193 @@ _scratch[3] = clamp(_scratch[3], 0, 24-1)+1; - iRect r(_scratch[0], + window = iRect(_scratch[0], _scratch[1], _scratch[2] - _scratch[0], _scratch[3] - _scratch[1] ); - - _textPort.frame = r; - - screen->setCursor(&_textPort, 0, 0); + cursor = window.origin; } - - | 0x03 ${ - /* clear from beginning of line */ - // todo -- should also include the cursor. - screen->erase(&_textPort, Screen::EraseLineBeforeCursor); - } - - | 0x04 ${ - /* pop text port */ - if (!_tpStack.empty()) { - _textPort = _tpStack.back(); - _tpStack.pop_back(); + | 0x03 ${ + /* + Clear from Beginning of Line + + Clears all characters from the left edge to and including the cursor. Sets them to the current consFill character. + */ + + char_info ci = remap_fill(_context); + iRect tmp( + iPoint(window.minX(), cursor.y), + iPoint(cursor.x+1, cursor.y+1) + ); + screen->fillRect(tmp, ci); + + } + + | 0x04 ${ + /* + Pop Text Port + + Restores the text port to the most recently saved value (see code $01). If no saved ports exist, resets the text port to the default values. If an 80-column text port is pushed and subsequently restored in 40-column mode, the text port may be altered to fit in the 40-column screen (see code $11, Set 40-Column Mode). + */ + + if (!_context_stack.empty()) { + _context = _context_stack.back(); + _context_stack.pop_back(); } else { - _textPort = TextPort(); + _context = gsos_context(); } } | 0x05 any ${ - /* horizontal scroll */ + /* + Horizontal Scroll + + Interprets the next byte as an 8-bit signed integer depicting the number (N) of + columns to shift. N equal to zero is a null operation. If N is less than zero, the text + port is shifted to the left; A greater than zero shifts to the right. If the shift magnitude is equal to or greater than windWidth, the text port is cleared. + The shifted characters are moved directly to their destination location. The space vacated by the shifted characters is set to the current consFill character (see the description of consFill earlier in this chapter). Characters shifted out of the text port are removed from the screen and are not recoverable. + */ int8_t n = fc; - // .... + // .... TODO } | 0x06 any ${ - /* set vertical position */ - unsigned n = clamp(fc - 32, 0, 24-1); - screen->setY(&_textPort, n); + /* + Set Vertical Position + + Interprets the next byte as a text port-relative vertical position + 32. If the destination is outside the current text port, the cursor is moved to the nearest edge. + */ + unsigned n = clamp(fc - 32, 0, window.height()-1); + cursor.y = window.minY() + n; } | 0x07 ${ + /* + Ring Bell + + Causes the System Beep to be played. It has no effect on the screen. + */ NSBeep(); } | 0x08 ${ - /* back space */ - screen->decrementX(&_textPort); + /* + Backspace + + Moves the cursor one position to the left. If the cursor was on the left edge of the + text port and consWrap is TRUE, the cursor is placed one row higher and at the right edge. If the cursor was also on the topmost row and consScroll is TRUE, thetext port will scroll backward one line. + */ + if (cursor.x > window.minX()) cursor.x--; + else if (_context.consWrap) { + cursor.x = window.maxX() - 1; + if (cursor.y > window.minY()) cursor.y--; + else if (_context.consScroll) screen->scrollDown(window); + } + } | 0x09 $nop | 0x0a ${ - /* line feed */ - screen->lineFeed(&_textPort); + /* + Line Feed + + Causes the cursor to move down one line. If the cursor was at the bottom edge of the text port and consScroll is TRUE, the text port scrolls up one line. + */ + if (cursor.y < window.maxY()-1) cursor.y++; + else if (_context.consScroll) + screen->scrollUp(window); } | 0x0b ${ - /* clear to end of text port */ - /* actually sets them to consFill */ - screen->erase(&_textPort, Screen::EraseAfterCursor); + /* + Clear to End of Text Port + + Clears all characters from the cursor to the end of the current text port and sets them to be equal to the current consFill character. + */ + + iRect tmp; + tmp.origin = cursor; + tmp.size = iSize(window.size.width - cursor.x, 1); + + screen->eraseRect(tmp); + + tmp = window; + tmp.origin.y = cursor.y+1; + tmp.size.height -= cursor.y+1; + screen->eraseRect(tmp); } - + | 0x0c ${ - /* clear text port and home */ - screen->erase(&_textPort, Screen::EraseAll); - screen->setCursor(&_textPort, 0, 0); + /* + Clear Text Port and Home Cursor + + Clears the entire text port and resets the cursor to windLeft, windTop. + */ + + screen->eraseRect(window); + cursor = window.origin; } | 0x0d ${ - /* carriage return */ - screen->setX(&_textPort, 0); - - if (_consLF) { - screen->lineFeed(&_textPort); + /* + Carriage Return + + Resets the cursor to the left edge of the text port; if consLF is TRUE, performs a line feed (see $0A, Line Feed). + */ + + cursor.x = window.minX(); + if (_context.consLF) { + if (cursor.y < window.maxY() - 1) cursor.y++; + else if (_context.consScroll) screen->scrollUp(window); } - } | 0x0e ${ - /* set normal display */ - screen->clearFlagBit(Screen::FlagInverse); + /* + Set Normal Display Mode + + After this character, displays all subsequent characters in normal mode, + */ + _context.clearFlagBit(Screen::FlagInverse); + _context.consFill = 0xa0; + _context.consVideo = true; } | 0x0f ${ - /* set inverse display */ - screen->setFlagBit(Screen::FlagInverse); + /* + Set Inverse Display Mode + After this character, displays all subsequent characters in inverse mode. + */ + _context.setFlagBit(Screen::FlagInverse); + _context.consFill = 0x20; + _context.consVideo = false; } | 0x10 any ${ - /* DLE expansion */ - if (_consDLE) { + /* + DLE Space Expansion + + If consDLE is TRUE, interprets the next character as number of spaces + 32, and the + correct number of spaces is issued to die screen. If consDLE is FALSE, the DLE character is ignored and the following character is processed normally. + */ + if (_context.consDLE) { unsigned count = (fc - 32) & 0xff; - while (count--) screen->putc(&_textPort, ' '); + char_info ci = remap_fill(_context); + + if (_context.consAdvance) { + while (count--) { + screen->putc(' ', _context); + if (_context.consAdvance) advance(screen, _context); + } + } + else { + if (count) screen->putc(' ', _context); + } } else { fhold; } } @@ -150,110 +269,173 @@ | 0x12 ${ /* 80 column mode */ } | 0x13 ${ - /* clear from beginning of text port */ - screen->erase(&_textPort, Screen::EraseBeforeCursor); - // todo -- clears up to and including cursor location! + } | 0x14 any ${ - /* set horizontal position */ - unsigned n = clamp(fc - 32, 0, 80 - 1); - screen->setX(&_textPort, n); + /* + Set Horizontal Position + + Interprets the next byte as a text port-relative horizontal position + 32. If the destination is outside the current text port, the cursor is moved to the nearest edge. + */ + unsigned n = clamp(fc - 32, 0, window.width() - 1); + cursor.x = window.minX() + n; } | 0x15 any ${ - /* set cursor movement */ + /* + Set Cursor Movement Word + + Interprets the next byte as cursor movement control, and sets the values of these Boolean flags: + */ + unsigned flags = fc; - _textPort.advanceCursor = flags & 0x01; - _consLF = flags & 0x02; - if (flags & 0x04) { - _textPort.leftMargin = TextPort::MarginWrap; - _textPort.rightMargin = TextPort::MarginWrap; - } else { - _textPort.leftMargin = TextPort::MarginTruncate; - _textPort.rightMargin = TextPort::MarginTruncate; - } - _textPort.scroll = flags & 0x08; - - _consDLE = flags & 0x10; + _context.consAdvance = flags & 0x01; + _context.consLF = flags & 0x02; + _context.consWrap = flags & 0x04; + _context.consScroll = flags & 0x08; + _context.consDLE = flags & 0x10; } - | 0x16 ${ /* scroll down 1 line */ } + | 0x16 ${ + /* + Scroll Down One Line + + Scrolls the text port down one line. Does not move the cursor. + */ + screen->scrollDown(window); + } - | 0x17 ${ /* scroll up one line */ } + | 0x16 ${ + /* + Scroll Up One Line + + Scrolls the text port up one line. Does not move the cursor. + */ + screen->scrollUp(window); + } | 0x18 ${ - /* disable mouse text */ - screen->clearFlagBit(Screen::FlagMouseText); + /* + Disable MouseText Mapping + + When MouseText is disabled, uppercase inverse characters are displayed as such (see the section "Character Set Mapping" earlier in this chapter). + */ + _context.clearFlagBit(Screen::FlagMouseText); + _context.consMouse = false; } | 0x19 ${ - /* home cursor */ - screen->setCursor(&_textPort, 0, 0); + /* + Home Cursor + + Resets the cursor to the upper-left corner of the text port. + */ + cursor = window.origin; } | 0x1a ${ - /* clear line */ - screen->erase(&_textPort, Screen::EraseLineAll); - screen->setX(&_textPort, 0); + /* + Clear Line + + Clears the line that the cursor is on. Resets the cursor to the leftmost column in the window. + */ + iRect tmp; + tmp.origin = iPoint(window.origin.x, cursor.y); + tmp.size = iSize(window.size.width, 1); + screen->eraseRect(tmp); } | 0x1b ${ - /* enable mouse text mapping */ - screen->setFlagBit(Screen::FlagMouseText); + /* + Enable MouseText Mapping + + When MouseText is enabled, uppercase inverse letters are displayed as MouseText symbols (see the section "Character Set Mapping" earlier in this chapter). + */ + _context.setFlagBit(Screen::FlagMouseText); + _context.consMouse = true; } | 0x1c ${ - /* move cursor right */ - screen->incrementX(&_textPort); + /* + Move Cursor Right + + Performs a nondestructive forward-space of the cursor. If consWrap is TRUE, + the cursor may go to the next line; if consScroll is TRUE, the screen may scroll up one line. + */ + advance(screen, _context); } | 0x1d ${ - /* clear to end of line */ - screen->erase(&_textPort, Screen::EraseLineAfterCursor); + /* + Clear to End of Line + + Clears from the position underneath the cursor to the end of the current line. + */ + iRect tmp; + tmp.origin = cursor; + tmp.size = iSize(window.size.width - cursor.x, 1); + + screen->eraseRect(tmp); + } - | 0x1e arg1 arg2 - ${ /* goto x y */ + | 0x1e arg1 arg2 ${ + /* + Go to X,Y + + Adjusts the cursor position relative to the text port. The parameters passed are X+32 + and Y+32. If the new locations are outside the current text port, the cursor is placed on the nearest edge. + */ + iPoint dca; - dca.x = clamp(_scratch[0], 0, 80 - 1); - dca.y = clamp(_scratch[1], 0, 24 - 1); - screen->setCursor(&_textPort, dca); + + dca.x = clamp(_scratch[0], 0, window.width() - 1); + dca.y = clamp(_scratch[1], 0, window.height() - 1); + cursor.x = dca.x + window.minX(); + cursor.y = dca.y + window.minY(); + cursor = dca; } | 0x1f ${ - /* move cursor up */ - screen->reverseLineFeed(&_textPort); + /* + Move Cursor Up + + Moves the cursor up one line (reverse line feed). If the cursor is already on the + uppermost line of the textport and consScroll isTRUE, it will cause a reverse scroll. + */ + if (cursor.y > window.minY()) cursor.y--; + else if (_context.consScroll) screen->scrollDown(window); } | 0x20 .. 0x7f ${ - screen->putc(&_textPort, fc); - } + screen->putc(fc, _context); + } $advance_if | 0x80 .. 0x9f ${ /* uppercase inverse/normal */ - uint8_t flag = ~(screen->flag() & Screen::FlagInverse); - screen->putc(&_textPort, fc - 0x40, flag); - } + uint8_t flags = ~(_context.flags & Screen::FlagInverse); + screen->putc(fc - 0x40, cursor, flags); + } $advance_if | 0xa0 .. 0xbf ${ /* special inverse/normal */ - uint8_t flag = ~(screen->flag() & Screen::FlagInverse); - screen->putc(&_textPort, fc - 0x80, flag); - } + uint8_t flags = ~(_context.flags & Screen::FlagInverse); + screen->putc(fc - 0x80, cursor, flags); + } $advance_if | 0xc0 .. 0xdf ${ /* uppercase normal / mouse text. */ - uint8_t flag = ~(screen->flag() & Screen::FlagInverse); - if (flag) flag |= Screen::FlagMouseText; - screen->putc(&_textPort, fc - 0x80, flag); - } + uint8_t flags = ~(_context.flags & Screen::FlagInverse); + if (flags) flags |= Screen::FlagMouseText; + screen->putc(fc - 0x80, cursor, flags); + } $advance_if | 0xe0 .. 0xff ${ /* special inverse/normal */ - uint8_t flag = ~(screen->flag() & Screen::FlagInverse); - screen->putc(&_textPort, fc - 0x80, flag); - } + uint8_t flags = ~(_context.flags & Screen::FlagInverse); + screen->putc(fc - 0x80, cursor, flags); + } $advance_if )* $err{ fgoto main; }; @@ -319,21 +501,24 @@ %%write init; - _textPort.frame = iRect(0, 0, 80, 24); - _textPort.cursor = iPoint(0,0); - - _textPort.scroll = true; - _textPort.advanceCursor = true; - _textPort.leftMargin = TextPort::MarginWrap; - _textPort.rightMargin = TextPort::MarginWrap; + _context.window = iRect(0, 0, 80, 24); + _context.cursor = iPoint(0,0); + + _context.consWrap = true; + _context.consAdvance = true; + _context.consLF = true; + _context.consScroll = true; + _context.consVideo = true; + _context.consDLE = true; + _context.consMouse = false; + _context.consFill = 0xa0; + _cursorType = Screen::CursorTypeUnderscore; - _consLF = true; - _consDLE = true; - - // set flags to plain text. + } + -(void)processData:(const uint8_t *)data length: (size_t)length screen:(Screen *)screen output:(OutputChannel *)output { @@ -341,8 +526,10 @@ const uint8_t *p = data; const uint8_t *pe = data + length; + auto &cursor = _context.cursor; + auto &window = _context.window; %%write exec; - + screen->setCursor(cursor); } -(void)keyDown:(NSEvent *)event screen:(Screen *)screen output:(OutputChannel *)output diff --git a/TermWindowController.mm b/TermWindowController.mm index a819f96..0fb16f8 100644 --- a/TermWindowController.mm +++ b/TermWindowController.mm @@ -11,8 +11,8 @@ #import "CurveView.h" #import "EmulatorWindow.h" -#import "VT52.h" -#import "PTSE.h" +//#import "VT52.h" +//#import "PTSE.h" #import "Defaults.h" @@ -309,7 +309,8 @@ klass = [_parameters objectForKey: kClass]; if (!klass || ![klass conformsToProtocol: @protocol(Emulator)]) { - klass = [VT52 class]; + klass = Nil; + //klass = [VT52 class]; } o = [_parameters objectForKey: kForegroundColor]; diff --git a/Views/EmulatorView.mm b/Views/EmulatorView.mm index 3e111f1..3e601f6 100644 --- a/Views/EmulatorView.mm +++ b/Views/EmulatorView.mm @@ -101,7 +101,17 @@ } else { + unsigned char c; + switch (cursorType) { + default: + case Screen::CursorTypeUnderscore: c = '_'; break; + case Screen::CursorTypePipe: c = '|'; break; + case Screen::CursorTypeBlock: c = 0x80; break; + } + [_cursorImg release]; + _cursorImg = [[_charGen imageForCharacter: c] retain]; [self startCursorTimer]; + } @@ -341,7 +351,7 @@ { NSRect charRect = NSMakeRect(_paddingLeft + x * _charWidth, _paddingTop + y *_charHeight, _charWidth, _charHeight); //NSImage *img; - CharInfo ci = _screen.getc(x, y); + char_info ci = _screen.getc(iPoint(x, y)); unsigned flag = ci.flag; uint8_t c = ci.c; @@ -374,7 +384,14 @@ std::swap(currentBack, currentFront); } } - + + + if (_cursorType == Screen::CursorTypeBlock && _cursorOn && _screen.cursor() == iPoint(x, y)) { + + std::swap(currentBack, currentFront); + + } + if (currentBack != _backgroundColor) { [currentBack setFill]; @@ -417,14 +434,18 @@ // cursor. iPoint cursor = _screen.cursor(); - if (_cursorOn && iRect(minX, minY, maxX - minX, maxY - minY).contains(cursor)) + if (_cursorOn && iRect(minX, minY, maxX - minX, maxY - minY).contains(cursor) && _cursorType != Screen::CursorTypeBlock) { NSRect charRect = NSMakeRect(_paddingLeft + cursor.x * _charWidth, _paddingTop + cursor.y *_charHeight, _charWidth, _charHeight); + [_foregroundColor setFill]; + NSCompositingOperation op = NSCompositingOperationCopy; + //if (_cursorType == Screen::CursorTypeBlock) op = NSCompositingOperationXOR; [_cursorImg drawInRect: charRect - fromRect: NSZeroRect operation: NSCompositeCopy + fromRect: NSZeroRect + operation: op fraction: 1.0 respectFlipped: YES hints: nil]; @@ -514,6 +535,7 @@ //[self stopCursorTimer]; //_screen.setCursorType(Screen::CursorTypeNone); +#if 0 _screen.beginUpdate(); _screen.setX(0); @@ -524,11 +546,11 @@ _screen.putc(*cp); } - updateRect = _screen.endUpdate(); [self invalidateIRect: updateRect]; +#endif //[_emulator writeLine: @"[Process completed]"]; @@ -570,7 +592,7 @@ std::string rv; int offset = 0; while (offset < bytes.size()) { - int max = bytes.size() - offset; + size_t max = bytes.size() - offset; if (max > 16) max = 16; rv.append(hex.data() + offset * 3, max * 3); @@ -940,6 +962,7 @@ void ViewScreen::setSize(unsigned width, unsigned height, bool resizeView) void ViewScreen::setCursorType(CursorType cursorType) { Screen::setCursorType(cursorType); + [_view setCursorType: cursorType]; } diff --git a/cpp/Screen.cpp b/cpp/Screen.cpp index faf8988..c1c886e 100644 --- a/cpp/Screen.cpp +++ b/cpp/Screen.cpp @@ -11,35 +11,17 @@ #include -iPoint TextPort::absoluteCursor() const -{ - return iPoint(frame.origin.x + cursor.x, frame.origin.y + cursor.y); -} - Screen::Screen(unsigned height, unsigned width) { - _port.frame = iRect(0, 0, width, height); - _port.rightMargin = TextPort::MarginTruncate; - _port.rightMargin = TextPort::MarginTruncate; + _frame = iRect(0, 0, width, height); - _port.advanceCursor = true; - _port.scroll = true; - - - _flag = 0; - _screen.resize(height); - for (ScreenIterator iter = _screen.begin(); iter != _screen.end(); ++iter) - { - iter->resize(width); - } - - + for (auto &line : _screen) line.resize(width); } Screen::~Screen() @@ -69,13 +51,12 @@ iRect Screen::endUpdate() _updates.push_back(_updateCursor); } - for (UpdateIterator iter = _updates.begin(); iter != _updates.end(); ++iter) - { - maxX = std::max(maxX, iter->x); - maxY = std::max(maxY, iter->y); + for (auto &point : _updates) { + maxX = std::max(maxX, point.x); + maxY = std::max(maxY, point.y); - minX = std::min(minX, iter->x); - minY = std::min(minY, iter->y); + minX = std::min(minX, point.x); + minY = std::min(minY, point.y); } _lock.unlock(); @@ -83,726 +64,219 @@ iRect Screen::endUpdate() return iRect(iPoint(minX, minY), iSize(maxX + 1 - minX, maxY + 1 - minY)); } -void Screen::setFlag(uint8_t flag) + + + +void Screen::putc(uint8_t c, iPoint cursor, uint8_t flags) { - _flag = flag; -} - -void Screen::setFlagBit(uint8_t bit) -{ - _flag |= bit; -} -void Screen::clearFlagBit(uint8_t bit) -{ - _flag &= ~bit; -} - - - -void Screen::putc(TextPort *textPort, uint8_t c) -{ - putc(textPort, c, _flag); -} - -void Screen::putc(TextPort *textPort, uint8_t c, uint8_t flag) -{ - /* - * textport must be valid. - * cursor must be within textport. - */ - - - if (!textPort) textPort = &_port; - iPoint cursor = textPort->absoluteCursor(); - - // right margin is a special case. - if (textPort->cursor.x == textPort->frame.width() -1) - { - if (textPort->rightMargin == TextPort::MarginTruncate) return; - if (textPort->rightMargin == TextPort::MarginOverwrite) - { - _updates.push_back(cursor); - _screen[cursor.y][cursor.x] = CharInfo(c, flag); - return; - } - //if (textPort->rightMargin == TextPort::MarginWrap) - } + if (!_frame.contains(cursor)) return; + if (cursor.x >= _frame.maxX() || cursor.y >= _frame.maxY()) return; _updates.push_back(cursor); - _screen[cursor.y][cursor.x] = CharInfo(c, flag); - - if (textPort->advanceCursor) - { - incrementX(textPort); - } - + _screen[cursor.y][cursor.x] = char_info(c, flags); } - #pragma mark - #pragma mark Cursor manipulation. -/* - * sets cursor.x within the textport. - * if x is outside the textport and clampX is true, it will be clamped to 0/width-1 - * if x is outside the textport and clampX is false, x will not be updated. - * - * returns the new cursor.x - */ - -int Screen::setX(TextPort *textPort, int x) -{ - // honors clampX. - if (!textPort) textPort = &_port; - - bool clamp = textPort->clampX; - - if (x < 0) - { - if (clamp) textPort->cursor.x = 0; - } - else if (x >= textPort->frame.width()) - { - if (clamp) textPort->cursor.x = textPort->frame.width() - 1; - } - else - { - textPort->cursor.x = x; - } - - if (textPort != &_port) _port.cursor = textPort->absoluteCursor(); - - return textPort->cursor.x; -} - -/* - * sets cursor.y within the textport. - * if y is outside the textport and clampY is true, it will be clamped to 0/height-1 - * if y is outside the textport and clampY is false, y will not be updated. - * - * returns the new cursor.y - */ - -int Screen::setY(TextPort *textPort, int y) -{ - // honors clampY. - - if (!textPort) textPort = &_port; - - bool clamp = textPort->clampY; - - if (y < 0) - { - if (clamp) textPort->cursor.y = 0; - } - else if (y >= textPort->frame.height()) - { - if (clamp) textPort->cursor.y = textPort->frame.height() - 1; - } - else - { - textPort->cursor.y = y; - } - - if (textPort != &_port) _port.cursor = textPort->absoluteCursor(); - - return textPort->cursor.y; -} - -/* - * increments cursor.x within the textport. - * if rightMargin wraps, it will set x = 0 and incrementY (which may scroll) - * if rightMargin does not wrap, it will not be updated. - * - * returns the new cursor.x - */ -int Screen::incrementX(TextPort *textPort) -{ - // honors wrap, scroll. - if (!textPort) textPort = &_port; - - - if (textPort->cursor.x == textPort->frame.width() - 1) - { - if (textPort->rightMargin == TextPort::MarginWrap) - { - textPort->cursor.x = 0; - incrementY(textPort); - } - } - else - { - textPort->cursor.x++; - } - - if (textPort != &_port) _port.cursor = textPort->absoluteCursor(); - - return textPort->cursor.x; -} - -/* - * decrements cursor.x within the textport. - * if leftMargin wraps, it will set x = width - 1 and decrementY (which may scroll) - * if leftMargin does not wrap, it will not be updated. - * - * returns the new cursor.x - */ - -int Screen::decrementX(TextPort *textPort) -{ - // honors wrap, scroll. - if (!textPort) textPort = &_port; - - - if (textPort->cursor.x == 0) - { - if (textPort->leftMargin == TextPort::MarginWrap) - { - textPort->cursor.x = textPort->frame.width() - 1; - decrementY(textPort); - } - } - else - { - textPort->cursor.x--; - } - - if (textPort != &_port) _port.cursor = textPort->absoluteCursor(); - - return textPort->cursor.x; - -} - -/* - * increment cursor.y - * this is similar to lineFeed, except that it honors the scroll flag - * at the bottom of the screen. - * returns the new cursor.y - */ - -int Screen::incrementY(TextPort *textPort) -{ - // similar to linefeed, but honors scroll. - if (!textPort) textPort = &_port; - - if (textPort->scroll) - return lineFeed(textPort); - - if (textPort->cursor.y < textPort->frame.height() - 1) - return lineFeed(textPort); - - return textPort->cursor.y; -} - - -/* - * decrement cursor.y - * this is similar to revereseLineFeed, except that it honors the scroll flag - * at the top of the screen. - * returns the new cursor.y - */ -int Screen::decrementY(TextPort *textPort) -{ - // similar to reverseLineFeed, but will not scroll. - if (!textPort) textPort = &_port; - - if (!textPort) textPort = &_port; - - if (textPort->scroll) - return reverseLineFeed(textPort); - - - if (textPort->cursor.y > 0) - return reverseLineFeed(textPort); - - - return textPort->cursor.y; -} - - -void Screen::setCursor(TextPort *textPort,iPoint point) -{ - setX(textPort, point.x); - setY(textPort, point.y); -} - -void Screen::setCursor(TextPort *textPort, int x, int y) -{ - setX(textPort, x); - setY(textPort, y); -} - #pragma mark - #pragma mark Erase -void Screen::erase(EraseRegion region) -{ +void Screen::eraseScreen() { fillScreen(char_info()); } +void Screen::eraseRect(iRect rect) { fillRect(rect, char_info()); } - CharInfoIterator ciIter; - ScreenIterator screenIter; + +void Screen::fillScreen(char_info ci) { - if (region == EraseAll) - { - ScreenIterator end = _screen.end(); - for (screenIter = _screen.begin(); screenIter < end; ++screenIter) - { - std::fill(screenIter->begin(), screenIter->end(), CharInfo(0,0)); - } - _updates.push_back(iPoint(0,0)); - _updates.push_back(iPoint(width() - 1, height() - 1)); - - return; + for (auto &line : _screen) { + std::fill(line.begin(), line.end(), ci); } - - - // TODO -- be smart and check if cursor is at x = 0 (or x = _width - 1) - if (region == EraseBeforeCursor) - { - ScreenIterator end = _screen.begin() + y() - 1; - for (screenIter = _screen.begin(); screenIter < end; ++screenIter) - { - std::fill(screenIter->begin(), screenIter->end(), CharInfo(0,0)); - } - _updates.push_back(iPoint(0,0)); - _updates.push_back(iPoint(width() - 1, y())); - - region = EraseLineBeforeCursor; - } - - if (region == EraseAfterCursor) - { - ScreenIterator end = _screen.end(); - for (screenIter = _screen.begin() + y() + 1; screenIter < end; ++screenIter) - { - std::fill(screenIter->begin(), screenIter->end(), CharInfo(0,0)); - } - _updates.push_back(iPoint(0, y() + 1)); - _updates.push_back(iPoint(width() - 1, height() - 1)); - - region = EraseLineAfterCursor; - } - - if (region == EraseLineAll) - { - std::fill(_screen[y()].begin(), _screen[y()].end(), CharInfo(0,0)); - - _updates.push_back(iPoint(0, y())); - _updates.push_back(iPoint(width() - 1, y())); - - return; - } - - if (region == EraseLineBeforeCursor) - { - std::fill(_screen[y()].begin(), _screen[y()].begin() + x(), CharInfo(0,0)); - - _updates.push_back(iPoint(0, y())); - _updates.push_back(cursor()); - - return; - } - - if (region == EraseLineAfterCursor) - { - std::fill(_screen[y()].begin() + x(), _screen[y()].end(), CharInfo(0,0)); - - _updates.push_back(cursor()); - _updates.push_back(iPoint(width() - 1, y())); - - return; - } -} - - - -void Screen::erase(TextPort* textPort, EraseRegion region) -{ - if (!textPort) textPort = &_port; - - iRect frame = textPort->frame; - iPoint cursor = textPort->absoluteCursor(); - - - if (region == EraseAll) - { - //erase the current screen - - ScreenIterator begin = _screen.begin() + frame.minY(); - ScreenIterator end = _screen.begin() + frame.maxY(); - - for (ScreenIterator iter = begin; iter != end; ++iter) - { - CharInfoIterator begin = iter->begin() + frame.minX(); - CharInfoIterator end = iter->begin() + frame.maxX(); - - std::fill(begin, end, CharInfo(0, 0)); - } - - _updates.push_back(frame.origin); - _updates.push_back(iPoint(frame.maxX() - 1, frame.maxY() - 1)); - - - return; - } - - if (region == EraseLineAll) - { - - // erase the current line. - - ScreenIterator iter = _screen.begin() + cursor.y; - CharInfoIterator begin = iter->begin() + frame.minX(); - CharInfoIterator end = iter->begin() + frame.maxX(); - - std::fill(begin, end, CharInfo(0, 0)); - - _updates.push_back(iPoint(frame.minX(), cursor.y)); - _updates.push_back(iPoint(frame.maxX() - 1, cursor.y)); - - return; - } - - - if (region == EraseBeforeCursor) - { - // erase everything before the cursor - // part 1 -- erase all lines prior to the current line. - - ScreenIterator begin = _screen.begin() + frame.minY(); - ScreenIterator end = _screen.begin() + cursor.y; - - for (ScreenIterator iter = begin; iter != end; ++iter) - { - CharInfoIterator begin = iter->begin() + frame.minX(); - CharInfoIterator end = iter->begin() + frame.maxX(); - - std::fill(begin, end, CharInfo(0, 0)); - } - - _updates.push_back(frame.origin); - _updates.push_back(iPoint(frame.maxX() - 1, cursor.y - 1)); - - // handle rest below. - region = EraseLineBeforeCursor; - } - - if (region == EraseAfterCursor) - { - // erase everything after the cursor - // part 1 -- erase all lines after the current line. - - ScreenIterator begin = _screen.begin() + cursor.y + 1; - ScreenIterator end = _screen.begin() + frame.maxY(); - - if (begin < end) - { - - for (ScreenIterator iter = begin; iter != end; ++iter) - { - CharInfoIterator begin = iter->begin() + frame.minX(); - CharInfoIterator end = iter->begin() + frame.maxX(); - - std::fill(begin, end, CharInfo(0, 0)); - } - - _updates.push_back(iPoint(cursor.x, cursor.y + 1)); - _updates.push_back(iPoint(frame.maxX() - 1, frame.maxY() - 1)); - - - } - - region = EraseLineAfterCursor; - } - - - if (region == EraseLineBeforeCursor) - { - // erase the current line, before the cursor. - - ScreenIterator iter = _screen.begin() + cursor.y; - CharInfoIterator begin = iter->begin() + frame.minX(); - CharInfoIterator end = iter->begin() + cursor.x; - - std::fill(begin, end, CharInfo(0, 0)); - - _updates.push_back(iPoint(frame.minX(), cursor.y)); - _updates.push_back(iPoint(cursor.x - 1, cursor.y)); - - return; - } - - if (region == EraseLineAfterCursor) - { - // erase the current line, after the cursor. - - ScreenIterator iter = _screen.begin() + cursor.y; - CharInfoIterator begin = iter->begin() + cursor.x; - CharInfoIterator end = iter->begin() + frame.maxX(); - - std::fill(begin, end, CharInfo(0, 0)); - - _updates.push_back(iPoint(cursor.x, cursor.y)); - _updates.push_back(iPoint(frame.maxX() - 1, cursor.y)); - - return; - } - - - -} - - - -void Screen::eraseRect(iRect rect) -{ - - unsigned maxX = std::min(width(), rect.maxX()); - unsigned maxY = std::min(height(), rect.maxY()); - - CharInfo clear; - - for (unsigned y = rect.minY(); y < maxY; ++y) - { - for (unsigned x = rect.minX(); x < maxX; ++x) - { - _screen[y][x] = clear; - } - } - - _updates.push_back(rect.origin); - _updates.push_back(iPoint(maxX - 1, maxY - 1)); -} - - - -void Screen::lineFeed() -{ - // moves the screen up one row, inserting a blank line at the bottom. - - if (y() == height() - 1) - { - deleteLine(0); - } - else - { - _port.cursor.y++; - } -} - -/* - * perform a line feed. This increments Y. If Y was at the bottom of the - * textPort, the textPort scrolls. - * - */ -int Screen::lineFeed(TextPort *textPort) -{ - - if (!textPort) - { - lineFeed(); - return y(); - } - - - if (textPort->cursor.y == textPort->frame.height() - 1) - { - deleteLine(textPort, 0); - } - else - { - textPort->cursor.y++; - if (textPort != &_port) _port.cursor = textPort->absoluteCursor(); - } - - return textPort->cursor.y; -} - - - -/* - * perform a reverse line feed. This increments Y. If Y was at the top of the - * textPort, the textPort scrolls. - * - */ -int Screen::reverseLineFeed(TextPort *textPort) -{ - - if (!textPort) - { - reverseLineFeed(); - return y(); - } - - - if (textPort->cursor.y == 0) - { - insertLine(textPort, 0); - } - else - { - textPort->cursor.y--; - if (textPort != &_port) _port.cursor = textPort->absoluteCursor(); - } - - return textPort->cursor.y; -} - - - -void Screen::reverseLineFeed() -{ - // moves the cursor down one row, inserting a blank line at the top. - - if (y() == 0) - { - insertLine(0); - } - else - { - _port.cursor.y--; - } - -} - - -void Screen::insertLine(unsigned line) -{ - - if (line >= height()) return; - - if (line == height() - 1) - { - _screen.back().clear(); - _screen.back().resize(width()); - } - else - { - std::vector newLine; - ScreenIterator iter; - - _screen.pop_back(); - iter = _screen.insert(_screen.begin() + line, newLine); - iter->resize(width()); - } - - _updates.push_back(iPoint(0, line)); + _updates.push_back(iPoint(0,0)); _updates.push_back(iPoint(width() - 1, height() - 1)); } -// line is relative to the textView. -// textView has been constrained. -void Screen::insertLine(TextPort *textPort, int line) -{ - CharInfo ci; + + + +void Screen::fillRect(iRect rect, char_info ci) { + + rect = rect.intersection(_frame); + + if (!rect.valid()) return; + + if (rect == _frame) return fillScreen(ci); + + auto yIter = _screen.begin() + rect.origin.y; + auto yEnd = yIter + rect.size.height; - - if (!textPort) return insertLine(line); - - iRect frame(textPort->frame); - - int minY = frame.minY(); - int maxY = frame.maxY(); - - int minX = frame.minX(); - int maxX = frame.maxX(); - - if (line < 0) return; - if (line >= frame.height()) return; + while (yIter != yEnd) { + auto &line = *yIter++; + auto xIter = line.begin() + rect.origin.x; + auto xEnd = xIter + rect.size.width; - // move all subsequent lines forward by 1. - for (int y = frame.height() - 2; y >= line; --y) - { - CharInfoIterator iter; - - iter = _screen[minY + y].begin(); - - std::copy(iter +minX, iter + maxX, _screen[minY + y + 1].begin() + minX); + std::fill(xIter, xEnd, ci); } - - // clear the line. - std::fill(_screen[minY + line].begin() + minX, _screen[minY + line].begin() + maxX, ci); - - // set the update region. - - _updates.push_back(iPoint(minX, minY + line)); - _updates.push_back(iPoint(maxX - 1, maxY - 1)); - -} - -void Screen::deleteLine(unsigned line) -{ - - if (line >= height()) return; - - if (line == height() - 1) - { - _screen.back().clear(); - - } - else - { - std::vector newLine; - - _screen.erase(_screen.begin() + line); - - _screen.push_back(newLine); - } - - _screen.back().resize(width()); - - - _updates.push_back(iPoint(0, line)); - _updates.push_back(iPoint(width() - 1, height() - 1)); + _updates.push_back(rect.origin); + _updates.push_back(iPoint(rect.maxX()-1, rect.maxY()-1)); } -void Screen::deleteLine(TextPort *textPort, int line) + + +template< class BidirIt1, class BidirIt2, class FX > +BidirIt2 copy_backward(BidirIt1 first, BidirIt1 last, BidirIt2 d_last, FX fx) { - CharInfo ci; - - if (!textPort) return deleteLine(line); - - iRect frame(textPort->frame); - - int minY = frame.minY(); - int maxY = frame.maxY(); - - int minX = frame.minX(); - int maxX = frame.maxX(); - - if (line < 0) return; - if (line >= frame.height()) return; - - // move all subsequent lines back by 1. - for (int y = line; y < frame.height() - 1; ++y) - { - CharInfoIterator iter; - CharInfoIterator end; + while (first != last) { - iter = _screen[minY + y + 1].begin(); - - std::copy(iter + minX, iter + maxX, _screen[minY + y].begin() + minX); + fx(*(--last), *(--d_last)); } + return d_last; +} + +template +OutputIt copy_forward(InputIt first, InputIt last, OutputIt d_first, FX fx) +{ + while (first != last) { + fx( *first, *d_first); + ++first; + ++d_first; + } + return d_first; +} + +void Screen::scrollUp() +{ + // save the first line (to avoid allocation/deallocation) + std::vector tmp; + std::swap(tmp, _screen.front()); + std::fill(tmp.begin(), tmp.end(), char_info()); + + auto iter = std::move(_screen.begin() + 1, _screen.end(), _screen.begin()); - // clear the last line. - std::fill(_screen[maxY - 1].begin() + minX, _screen[maxY - 1].begin() + maxX, ci); + *iter = std::move(tmp); - // set the update region. + _updates.push_back(iPoint(0,0)); + _updates.push_back(iPoint(width() - 1, height() - 1)); +} + + +void Screen::scrollUp(iRect rect) +{ - _updates.push_back(iPoint(minX, minY + line)); - _updates.push_back(iPoint(maxX - 1, maxY - 1)); + rect = rect.intersection(_frame); + + if (!rect.valid()) return; + + if (rect == _frame) return scrollUp(); + + + auto src = _screen.begin() + rect.minY()+1; + auto dest = _screen.begin() + rect.minY(); + auto end = _screen.begin() + rect.maxY(); + + auto iter = copy_forward(src, end, dest, [=](const auto &src, auto &dest){ + std::copy(src.begin() + rect.minX(), src.begin() + rect.maxX(), dest.begin()); + }); + + std::fill(iter->begin() + rect.minX(), iter->begin() + rect.maxX(), char_info()); + + + _updates.push_back(rect.origin); + _updates.push_back(iPoint(rect.maxX()-1, rect.maxY()-1)); +} + + + +void Screen::scrollDown() +{ + + // save the first line (to avoid allocation/deallocation) + std::vector tmp; + std::swap(tmp, _screen.back()); + std::fill(tmp.begin(), tmp.end(), char_info()); + + auto iter = std::move_backward(_screen.begin(), _screen.end()-1, _screen.end()); + + --iter; + *iter = std::move(tmp); + + _updates.push_back(iPoint(0,0)); + _updates.push_back(iPoint(width() - 1, height() - 1)); } +void Screen::scrollDown(iRect rect) +{ + + rect = rect.intersection(_frame); + + if (!rect.valid()) return; + + if (rect == _frame) return scrollDown(); + + auto src = _screen.begin() + rect.minY(); + auto end = _screen.begin() + rect.maxY()-1; + auto dest = _screen.begin() + rect.maxY(); + + auto iter = copy_backward(src, end, dest, [=](const auto &src, auto &dest) { + std::copy(src.begin() + rect.minX(), src.begin() + rect.maxX(), dest.begin()); + }); + --iter; + std::fill(iter->begin() + rect.minX(), iter->begin() + rect.maxX(), char_info()); + + + _updates.push_back(rect.origin); + _updates.push_back(iPoint(rect.maxX()-1, rect.maxY()-1)); +} + + + + + +#if 0 +void Screen::scrollDown(iRect rect) +{ + + rect = rect.intersection(_frame); + + if (!rect.valid()) return; + + if (rect == _frame) return scrollDown(); + + + auto src = _screen.begin() + rect.maxY()-1; + auto dest = _screen.begin() + rect.maxY(); + auto end = _screen.begin() + rect.minY(); + + + while (src != end) { + --src; + --dest; + + std::copy(src->begin() + rect.minX(), src->begin() + rect.maxX(), dest->begin() + rect.minX()); + + } + + auto &line = _screen[rect.minY()]; + std::fill(line.begin() + rect.minX(), line.begin() + rect.maxX(), char_info()); + + _updates.push_back(rect.origin); + _updates.push_back(iPoint(rect.maxX()-1, rect.maxY()-1)); +} +#endif + + + + void Screen::setSize(unsigned w, unsigned h) { @@ -810,49 +284,15 @@ void Screen::setSize(unsigned w, unsigned h) if ((height() == h) && (width() == w)) return; - if (height() < h) - { - _screen.resize(h); + _screen.resize(h); + for (auto &line : _screen) { + line.resize(w); } - else if (height() > h) - { - unsigned count = height() - h; - int y = _port.cursor.y; - int maxY = height() - 1; - - // 1. erase from the bottom, up to the cursor (if blank) - // 2. erase lines from the top. - - while (count && maxY > y) - { - // todo -- check if blank... - _screen.pop_back(); - --count; - --maxY; - } - - - // erase lines from the top. - if (count) - _screen.erase(_screen.begin(), _screen.begin() + count); - } - - //if (_width != _width || _height != height) - { - ScreenIterator iter; - for (iter = _screen.begin(); iter != _screen.end(); ++iter) - { - iter->resize(w); - } + _frame.size = iSize(w, h); - } - - _port.frame.size = iSize(w, h); - - - if (_port.cursor.y >= h) _port.cursor.y = h - 1; - if (_port.cursor.x >= w) _port.cursor.x = w - 1; + if (_cursor.y >= h) _cursor.y = h - 1; + if (_cursor.x >= w) _cursor.x = w - 1; //fprintf(stderr, "setSize(%u, %u)\n", width, height); diff --git a/cpp/Screen.h b/cpp/Screen.h index 35ee30f..418e5d2 100644 --- a/cpp/Screen.h +++ b/cpp/Screen.h @@ -17,45 +17,27 @@ -typedef struct CharInfo { +typedef struct char_info { - CharInfo() = default; - CharInfo(uint8_t cc, uint8_t ff) : c(cc), flag(ff) {} + char_info() = default; + char_info(uint8_t cc, uint8_t ff) : c(cc), flag(ff) {} uint8_t c = 0; uint8_t flag = 0; -} CharInfo; +} char_info; -typedef struct TextPort { - - enum MarginBehavior { - MarginTruncate, - MarginWrap, - MarginOverwrite - }; - - - - iRect frame; +typedef struct context { + uint8_t flags = 0; + iRect window; iPoint cursor; + + void setFlagBit(unsigned x) { flags |= x; } + void clearFlagBit(unsigned x) { flags &= ~x; } +} context; - MarginBehavior leftMargin = MarginTruncate; - MarginBehavior rightMargin = MarginTruncate; - - bool advanceCursor = true; - bool scroll = true; - - // clamp setCursor calls. - bool clampX = true; - bool clampY = true; - - - iPoint absoluteCursor() const; - -} TextPort; class Screen { @@ -72,7 +54,7 @@ public: static const unsigned FlagSelected = 0x8000; - +/* enum EraseRegion { EraseAll, EraseBeforeCursor, @@ -82,7 +64,7 @@ public: EraseLineBeforeCursor, EraseLineAfterCursor }; - +*/ enum CursorType { CursorTypeNone, CursorTypeUnderscore, @@ -101,85 +83,42 @@ public: int y() const; iPoint cursor() const; - uint8_t flag() const; int height() const; int width() const; - int incrementX(bool clamp = true); - int decrementX(bool clamp = true); - int incrementY(bool clamp = true); - int decrementY(bool clamp = true); - - void setX(int x, bool clamp = true); - void setY(int y, bool clamp = true); - - - void setCursor(iPoint point, bool clampX = true, bool clampY = true); - void setCursor(int x, int y, bool clampX = true, bool clampY = true); + void setCursor(iPoint point); - int setX(TextPort *textPort, int x); - int setY(TextPort *textPort, int y); + + void putc(uint8_t c, iPoint cursor, uint8_t flags = 0); + void putc(uint8_t c, const context &ctx) { putc(c, ctx.cursor, ctx.flags); } + + char_info getc(iPoint p) const; + - int incrementX(TextPort *textPort); - int incrementY(TextPort *textPort); - - int decrementX(TextPort *textPort); - int decrementY(TextPort *textPort); - - void setCursor(TextPort *textPort, iPoint point); - void setCursor(TextPort *textPort, int x, int y); - - - - void setFlag(uint8_t flag); - void setFlagBit(uint8_t bit); - void clearFlagBit(uint8_t bit); - - - void putc(uint8_t c, bool incrementX = true); - void putc(TextPort *textPort, uint8_t c); - void putc(TextPort *textPort, uint8_t c, uint8_t flags); - - - CharInfo getc(int x, int y) const; - - void deletec(); - void insertc(uint8_t c); - - void tabTo(unsigned x); - void tabTo(TextPort *textPort, unsigned x); - - - void erase(EraseRegion); - void erase(TextPort *, EraseRegion); - - void eraseLine(); void eraseScreen(); - void eraseRect(iRect rect); + void fillScreen(char_info ci); + void fillRect(iRect rect, char_info ci); - void lineFeed(); - int lineFeed(TextPort *textPort); - - void reverseLineFeed(); - int reverseLineFeed(TextPort *textPort); + + void scrollUp(); + void scrollUp(iRect window); + + void scrollDown(); + void scrollDown(iRect window); void deleteLine(unsigned line); void insertLine(unsigned line); - void insertLine(TextPort *textPort, int line); - void deleteLine(TextPort *textPort, int line); + //void deletec(); + //void insertc(uint8_t c); - void insertc(TextPort *textPort, uint8_t c); - void deletec(TextPort *textPort); - - void beginUpdate(); iRect endUpdate(); @@ -187,36 +126,32 @@ public: void lock(); void unlock(); - void setTextPort(const TextPort& textPort); virtual void setSize(unsigned width, unsigned height); virtual void setCursorType(CursorType cursor); - CursorType cursorType() const; private: - TextPort _port; - - - uint8_t _flag; + iRect _frame; + iPoint _cursor; CursorType _cursorType; Lock _lock; - std::vector< std::vector< CharInfo > > _screen; + std::vector< std::vector< char_info > > _screen; std::vector _updates; iPoint _updateCursor; - typedef std::vector< std::vector< CharInfo > >::iterator ScreenIterator; - typedef std::vector< std::vector< CharInfo > >::reverse_iterator ReverseScreenIterator; + typedef std::vector< std::vector< char_info > >::iterator ScreenIterator; + typedef std::vector< std::vector< char_info > >::reverse_iterator ReverseScreenIterator; - typedef std::vector::iterator CharInfoIterator; + typedef std::vector::iterator CharInfoIterator; typedef std::vector::iterator UpdateIterator; }; @@ -224,23 +159,19 @@ private: inline int Screen::x() const { - return _port.cursor.x; + return _cursor.x; } inline int Screen::y() const { - return _port.cursor.y; + return _cursor.y; } inline iPoint Screen::cursor() const { - return _port.cursor; + return _cursor; } -inline uint8_t Screen::flag() const -{ - return _flag; -} inline Screen::CursorType Screen::cursorType() const { @@ -249,24 +180,20 @@ inline Screen::CursorType Screen::cursorType() const inline int Screen::height() const { - return _port.frame.size.height; + return _frame.size.height; } inline int Screen::width() const { - return _port.frame.size.width; + return _frame.size.width; } -inline void Screen::setCursor(iPoint point, bool clampX, bool clampY) +inline void Screen::setCursor(iPoint point) { - setX(point.x, clampX); - setY(point.y, clampY); -} - -inline void Screen::setCursor(int x, int y, bool clampX, bool clampY) -{ - setX(x, clampX); - setY(y, clampY); + if (point.x >= _frame.width()) point.x = _frame.width() - 1; + if (point.y >= _frame.height()) point.y = _frame.height() - 1; + + _cursor = point; } @@ -281,12 +208,10 @@ inline void Screen::unlock() } -inline CharInfo Screen::getc(int x, int y) const +inline char_info Screen::getc(iPoint p) const { - if (x < 0 || y < 0) return CharInfo(); - if (x >= width() || y >= height()) return CharInfo(0,0); - - return _screen[y][x]; + if (_frame.contains(p)) return _screen[p.y][p.x]; + return char_info(); } #endif diff --git a/cpp/Screen_TextPort.cpp b/cpp/Screen_TextPort.cpp deleted file mode 100644 index cce2d7c..0000000 --- a/cpp/Screen_TextPort.cpp +++ /dev/null @@ -1,124 +0,0 @@ -// -// Screen_TextPort.cpp -// 2Term -// -// Created by Kelvin Sherlock on 1/11/2011. -// Copyright 2011 __MyCompanyName__. All rights reserved. -// - -#include "Screen.h" - - - -void Screen::setTextPort(const TextPort& textPort) -{ - TextPort tmp(textPort); - - // call virtual method... - setSize(textPort.frame.width(), textPort.frame.height()); - - tmp.frame.origin = iPoint(0, 0); - _port = tmp; -} - -/* - * Non-destructive tab. - * Sets the horizontal cursor position, may wrap and scroll - * - * - */ -void Screen::tabTo(TextPort *textPort, unsigned xPos) -{ - - if (!textPort) textPort = &_port; - - iRect frame = textPort->frame; - - // best case -- no wrapping needed. - if (xPos < frame.width()) - { - textPort->cursor.x = xPos; - - } - else if (textPort->rightMargin == TextPort::MarginWrap) - { - // worst case -- wrapping needed. - textPort->cursor.x = 0; - incrementY(textPort); - - if (textPort != &_port) _port.cursor = textPort->absoluteCursor(); - - return; - } - else - { - // clamp to right margin. - textPort->cursor.x = frame.width() - 1; - } - - if (textPort != &_port) _port.cursor = textPort->absoluteCursor(); - - return; -} - -// insert a character at the current cursor position, -// moving all characters right 1. -// no wrapping is performed. -void Screen::insertc(TextPort *textPort, uint8_t c) -{ - if (!textPort) textPort = &_port; - - iRect frame = textPort->frame; - iPoint cursor = textPort->cursor; - iPoint absoluteCursor = textPort->absoluteCursor(); - - if (cursor.x >= frame.width()) return; - if (cursor.y >= frame.height()) return; - - CharInfoIterator iter = _screen[absoluteCursor.y].begin(); - CharInfoIterator begin = iter + absoluteCursor.x; - CharInfoIterator end = iter + frame.maxX(); - - CharInfo ci(c, _flag); - // move all chars forward 1. - for (iter = begin; iter < end; ++iter) - { - std::swap(ci, *iter); - } - - _updates.push_back(absoluteCursor); - _updates.push_back(iPoint(frame.maxX(), absoluteCursor.y)); -} - -// delete the character at the current cursor position, -// moving any character to the right left 1 spot -// the final position is blank filled. -// no wrapping is performed. -void Screen::deletec(TextPort *textPort) -{ - if (!textPort) textPort = &_port; - - iRect frame = textPort->frame; - iPoint cursor = textPort->cursor; - iPoint absoluteCursor = textPort->absoluteCursor(); - - if (cursor.x >= frame.width()) return; - if (cursor.y >= frame.height()) return; - - CharInfoIterator iter = _screen[absoluteCursor.y].begin(); - CharInfoIterator begin = iter + absoluteCursor.x; - CharInfoIterator end = iter + frame.maxX() - 1; - - - for (iter = begin; iter < end; ++iter) - { - iter[0] = iter[1]; - - } - - // not sure about the flag situation... - *iter = CharInfo(' ', _flag); - - _updates.push_back(absoluteCursor); - _updates.push_back(iPoint(frame.maxX(), absoluteCursor.y)); -} \ No newline at end of file diff --git a/cpp/Screen_obsolete.cpp b/cpp/Screen_obsolete.cpp deleted file mode 100644 index d366c88..0000000 --- a/cpp/Screen_obsolete.cpp +++ /dev/null @@ -1,183 +0,0 @@ -// -// Screen_obsolete.cpp -// 2Term -// -// Created by Kelvin Sherlock on 1/11/2011. -// Copyright 2011 __MyCompanyName__. All rights reserved. -// - -#include "Screen.h" - -void Screen::setX(int x, bool clamp) -{ - if (x < 0) - { - if (clamp) _port.cursor.x = 0; - return; - } - if (x >= width()) - { - if (clamp) _port.cursor.x = width() - 1; - return; - } - - _port.cursor.x = x; -} - - -void Screen::setY(int y, bool clamp) -{ - if (y < 0) - { - if (clamp) _port.cursor.y = 0; - return; - } - if (y >= height()) - { - if (clamp) _port.cursor.y = height() - 1; - return; - } - - _port.cursor.y = y; -} - -int Screen::incrementX(bool clamp) -{ - setX(_port.cursor.x + 1, clamp); - return _port.cursor.x; -} - -int Screen::decrementX(bool clamp) -{ - setX(_port.cursor.x - 1, clamp); - return _port.cursor.x; -} - -int Screen::incrementY(bool clamp) -{ - setY(_port.cursor.y + 1, clamp); - return _port.cursor.y; -} - -int Screen::decrementY(bool clamp) -{ - setY(_port.cursor.y - 1, clamp); - return _port.cursor.y; -} - -void Screen::tabTo(unsigned xPos) -{ - CharInfo clear(' ', _flag); - CharInfoIterator iter; - - xPos = std::min((int)xPos, width() - 1); - - - _updates.push_back(_port.cursor); - _updates.push_back(iPoint(xPos, _port.cursor.y)); - - for (unsigned x = _port.cursor.x; x < xPos; ++x) - { - _screen[_port.cursor.y][x] = clear; - } - _port.cursor.x = xPos; -} - - - - -void Screen::putc(uint8_t c, bool incrementX) -{ - if (_port.cursor.x < width()) - { - _updates.push_back(_port.cursor); - - _screen[_port.cursor.y][_port.cursor.x] = CharInfo(c, _flag); - - if (incrementX && _port.cursor.x < width() - 1) ++_port.cursor.x; - } -} - - - -void Screen::deletec() -{ - // delete character at cursor. - // move following character up - // set final character to ' ' (retaining flags from previous char) - - if (_port.cursor.x >= width()) return; - - _updates.push_back(_port.cursor); - _updates.push_back(iPoint(width() - 1, _port.cursor.y)); - - - CharInfoIterator end = _screen[_port.cursor.y].end() - 1; - CharInfoIterator iter = _screen[_port.cursor.y].begin() + _port.cursor.x; - - - for ( ; iter != end; ++iter) - { - iter[0] = iter[1]; - - } - // retain the flags previously there. - end->c = ' '; -} - - -void Screen::insertc(uint8_t c) -{ - // insert character at cursor. - // move following characters up (retaining flags). - - if (_port.cursor.x >= width()) return; - - _updates.push_back(_port.cursor); - _updates.push_back(iPoint(width() - 1, _port.cursor.y)); - - CharInfoIterator end = _screen[_port.cursor.y].end() - 1; - CharInfoIterator iter = _screen[_port.cursor.y].begin() + _port.cursor.x; - - for ( ; iter != end; ++iter) - { - iter[1] = iter[0]; - } - - iter->c = ' '; -} - -void Screen::eraseLine() -{ - // erases everything to the right of, and including, the cursor - - for (CharInfoIterator ciIter = _screen[y()].begin() + x(); ciIter < _screen[y()].end(); ++ciIter) - { - *ciIter = CharInfo(0, _flag); - } - - _updates.push_back(cursor()); - _updates.push_back(iPoint(width() - 1, y())); -} - -void Screen::eraseScreen() -{ - // returns everything to the right of, and including, the cursor as well as all subsequent lines. - - eraseLine(); - - if (y() == height() -1) return; - - for (ScreenIterator iter = _screen.begin() + y(); iter < _screen.end(); ++iter) - { - for (CharInfoIterator ciIter = iter->begin(); ciIter < iter->end(); ++ciIter) - { - *ciIter = CharInfo(0, _flag); - } - - } - - _updates.push_back(iPoint(0, y() + 1)); - _updates.push_back(iPoint(width() - 1, height() - 1)); -} - diff --git a/cpp/iGeometry.cpp b/cpp/iGeometry.cpp index 99d3bbd..5c685f5 100644 --- a/cpp/iGeometry.cpp +++ b/cpp/iGeometry.cpp @@ -9,7 +9,7 @@ #include "iGeometry.h" - +#include bool iRect::contains(const iPoint aPoint) const { @@ -30,4 +30,19 @@ bool iRect::contains(const iRect aRect) const bool iRect::intersects(const iRect aRect) const { return aRect.contains(origin) || aRect.contains(origin.offset(size)); -} \ No newline at end of file +} + +iRect iRect::intersection(const iRect &rhs) const { + iPoint topLeft; + iPoint bottomRight; + topLeft.x = std::max(origin.x, rhs.origin.x); + topLeft.y = std::max(origin.y, rhs.origin.y); + + bottomRight.x = std::min(maxX(), rhs.maxX()); + bottomRight.y = std::min(maxY(), rhs.maxY()); + + if (bottomRight.x <= topLeft.x) return iRect(); + if (bottomRight.y <= topLeft.y) return iRect(); + + return iRect(topLeft, bottomRight); +} diff --git a/cpp/iGeometry.h b/cpp/iGeometry.h index 9bc89ec..7aac4a6 100644 --- a/cpp/iGeometry.h +++ b/cpp/iGeometry.h @@ -23,10 +23,10 @@ typedef struct iSize { iSize &operator=(const iSize &) = default; - bool operator==(const iSize &aSize) + bool operator==(const iSize &aSize) const { return width == aSize.width && height == aSize.height; } - bool operator!=(const iSize& aSize) + bool operator!=(const iSize& aSize) const { return !(*this == aSize); } } iSize; @@ -43,10 +43,10 @@ typedef struct iPoint { iPoint &operator=(const iPoint &) = default; - bool operator==(const iPoint &aPoint) + bool operator==(const iPoint &aPoint) const { return x == aPoint.x && y == aPoint.y; } - bool operator!=(const iPoint &aPoint) + bool operator!=(const iPoint &aPoint) const { return !(*this == aPoint); } iPoint offset(int dx, int dy) const @@ -80,6 +80,23 @@ typedef struct iRect { bool intersects(const iRect aRect) const; + iRect intersection(const iRect &rhs) const; + + bool operator==(const iRect &rhs) const { + return origin == rhs.origin && size == rhs.size; + } + bool operator!=(const iRect &rhs) const { + return !(*this == rhs); + } + + explicit operator bool() const { return size.height >= 0 && size.width >= 0; } + bool operator!() const { return size.height < 0 || size.width < 0; } + bool valid() const { return size.height >= 0 && size.width >= 0; } + + iPoint topLeft() const { return origin; } + iPoint bottomRight() const { return iPoint(maxX(), maxY()); } + + void setBottomLeft(iPoint &p); int height() const { return size.height; } diff --git a/cpp/ring_buffer.h b/cpp/ring_buffer.h index f6d20a0..65d8024 100644 --- a/cpp/ring_buffer.h +++ b/cpp/ring_buffer.h @@ -56,7 +56,7 @@ public: _capacity = 0; // fill up the end, then wrap around to start. if (_ptr + dsize > Size) { - int amt = Size - _ptr; + size_t amt = Size - _ptr; memcpy(_buffer + _ptr, data, amt); _ptr = 0; dsize -= amt;