From 99f5d60be847ca4caaa1ae749a912c14d0eee6c6 Mon Sep 17 00:00:00 2001 From: Kelvin Sherlock Date: Sat, 3 Mar 2018 10:23:57 -0500 Subject: [PATCH] rewrite VT05 / VT50 / VT50H with ragel. --- Emulators/VT05.h | 5 +- Emulators/VT05.mm.ragel | 236 ++++++++++++++++ Emulators/VT50.h | 33 +++ Emulators/VT50.mm.ragel | 447 ++++++++++++++++++++++++++++++ Emulators/VT52.h | 3 + Emulators/VT52.mm | 4 +- TwoTerm.xcodeproj/project.pbxproj | 8 + 7 files changed, 732 insertions(+), 4 deletions(-) create mode 100644 Emulators/VT05.mm.ragel create mode 100644 Emulators/VT50.h create mode 100644 Emulators/VT50.mm.ragel diff --git a/Emulators/VT05.h b/Emulators/VT05.h index 67e0b40..6fc4d27 100644 --- a/Emulators/VT05.h +++ b/Emulators/VT05.h @@ -15,11 +15,10 @@ @interface VT05 : NSObject { - unsigned _state; + unsigned cs; + unsigned _scratch[2]; context _context; - BOOL _upperCase; } --(void)tab: (Screen *)screen; @end diff --git a/Emulators/VT05.mm.ragel b/Emulators/VT05.mm.ragel new file mode 100644 index 0000000..bbf3b51 --- /dev/null +++ b/Emulators/VT05.mm.ragel @@ -0,0 +1,236 @@ +// +// VT05.mm.m +// TwoTerm +// +// Created by Kelvin Sherlock on 3/3/2018. +// +// Disabled because linefeed only scrolls when on the last line... not very useful! +// +// + +/* + * http://vt100.net/docs/vt05-rm/contents.html + */ + +#include +#include + +#import "VT05.h" + +#include "OutputChannel.h" +#include "Screen.h" + +enum { + VTBell = 07, + VTCursorLeft = 010, + VTTab = 011, + VTLineFeed = 012, + VTCursorDown = 013, + VTCarriageReturn = 015, + VTCAD = 016, + + VTCursorRight = 030, + VTCursorUp = 032, + VTHome = 035, + VTEOL = 036, + VTEOS = 037 + +}; + + +%%{ + machine console; + alphtype unsigned int; + + action nop {} + + + action tab { + if (cursor.x < 64) cursor.x = (cursor.x + 8) & ~7; + else if (cursor.x < window.maxX() -1) cursor.x++; + } + + action linefeed { + if (cursor.y == window.maxY() -1) + screen->scrollUp(); + } + + action dca { + + unsigned y = _scratch[0]; + if (y < window.maxY()) cursor.y = y; + + unsigned x = _scratch[1]; + if (x < window.maxX()) cursor.x = x; + } + + action erase_eos { + + iRect tmp; + + tmp.origin = cursor; + tmp.size = iSize( window.maxX() - cursor.x, 1); + screen->eraseRect(tmp); + + tmp.origin = iPoint(0, cursor.y+1); + tmp.size = iSize(window.maxX(), window.maxY() - cursor.y - 1); + screen->eraseRect(tmp); + + } + + action erase_eol { + + iRect tmp; + + tmp.origin = cursor; + tmp.size = iSize( window.maxX() - cursor.x, 1); + screen->eraseRect(tmp); + + } + + arg1 = 0x00* (any-0x00) ${ _scratch[0] = ((fc & 0x7f) - 32); }; + arg2 = 0x00* (any-0x00) ${ _scratch[1] = ((fc & 0x7f) - 32); }; + + control_codes = ( + 0x07 ${ NSBeep(); } + | 0x08 ${ if (cursor.x) cursor.x--; } + | 0x09 $tab + | 0x0a $linefeed + | 0x0b ${ if (cursor.y < window.maxY() -1) cursor.y++; } + | 0x0d ${ cursor.x = 0; } + | 0x0e arg1 arg2 $dca + | 0x18 ${ if (cursor.x < window.maxX() -1) cursor.x++; } + | 0x1a ${ if (cursor.y) cursor.y--; } + | 0x1d ${ cursor = iPoint(0,0); } + | 0x1e $erase_eol + | 0x1f $erase_eos + + ); + + main := ( + control_codes + | 0x20 .. 0x7e ${ + uint8_t c = fc; + if (c & 0x40) c &= ~0x20; + screen->putc(c, _context); + if (cursor.x < window.maxX() - 1) cursor.x++; + } + | any + + )** $err{ fgoto main; }; + + write data; +}%% + + +@implementation VT05 + ++(void)load { + [EmulatorManager registerClass: self]; +} + ++(NSString *)name { + return @"VT05"; +} + +-(NSString *)name { + return @"VT05"; +} + +-(const char *)termName { + return "vt05"; +} + +-(void)reset: (BOOL)hard { + + %% write init; + if (hard) { + _context.cursor = iPoint(0,0); + _context.window = iRect(0, 0, 72, 20); + } +} + +-(BOOL)resizable { + return NO; +} + +-(struct winsize)defaultSize { + struct winsize ws = { 20, 72, 0, 0 }; + return ws; +} + + +-(id)init { + if ((self = [super init])) { + [self reset: YES]; + } + return self; +} + +-(void)processData: (uint8_t *)data length: (size_t)length screen:(Screen *)screen output:(OutputChannel *)output +{ + std::transform(data, data + length, data, [](uint8_t c){ return c & 0x7f; }); + + const uint8_t *eof = nullptr; + const uint8_t *p = data; + const uint8_t *pe = data + length; + + iPoint &cursor = _context.cursor; + const iRect &window = _context.window; + + %%write exec; + + if (cursor.x == window.maxX()) screen->setCursor(iPoint(window.maxX() - 1, 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]; + uint8_t c; + + switch (uc) + { + case NSLeftArrowFunctionKey: + output->write(VTCursorLeft); + break; + case NSRightArrowFunctionKey: + output->write(VTCursorRight); + break; + case NSUpArrowFunctionKey: + output->write(VTCursorUp); + break; + case NSDownArrowFunctionKey: + output->write(VTCursorDown); + break; + case NSHomeFunctionKey: + output->write(VTHome); + break; + case NSDeleteCharacter: + output->write(0x7f); + break; + + default: + if (uc > 0x7f) break; + c = uc; + + if (flags & NSControlKeyMask) + { + c = CTRL(c); + } + output->write(c); + break; + } + } +} + + +@end diff --git a/Emulators/VT50.h b/Emulators/VT50.h new file mode 100644 index 0000000..06950bd --- /dev/null +++ b/Emulators/VT50.h @@ -0,0 +1,33 @@ +// +// VT50.h +// TwoTerm +// +// Created by Kelvin Sherlock on 3/2/2018. +// + +#import + +#import +#import "Emulator.h" + +#include "iGeometry.h" +#include "Screen.h" + +@interface VT50x : NSObject { + + + unsigned cs; + int _scratch[2]; + context _context; + unsigned _model; + BOOL _altKeyPad; + BOOL _graphics; +} + +@end + +@interface VT50 : VT50x +@end + +@interface VT50H : VT50x +@end diff --git a/Emulators/VT50.mm.ragel b/Emulators/VT50.mm.ragel new file mode 100644 index 0000000..b44d4f5 --- /dev/null +++ b/Emulators/VT50.mm.ragel @@ -0,0 +1,447 @@ +// +// VT50.m +// TwoTerm +// +// Created by Kelvin Sherlock on 3/2/2018. +// + +#include +#include +#include +#include +#include + +#import "VT50.h" +#include "OutputChannel.h" +#include "Screen.h" + +#define ESC "\x1b" + +enum { + ModelVT50, + ModelVT50H, + ModelVT52, + ModelVT55 +}; +namespace { + void normalize(iRect &r){ + r.origin.y <<= 1; + r.size.height <<= 1; + } + + void normalize(iPoint &p) { + p.y <<= 1; + } +} +%%{ + machine vt50; + alphtype unsigned int; + + + esc = 0x1b; + + + action vt50h { _model == ModelVT50H } + action vt50 { _model == ModelVT50 } + + + action tab { + if (cursor.x < 72) cursor.x = (cursor.x + 8) & ~7; + else if (cursor.x < window.maxX() -1) cursor.x++; + } + + action linefeed { + if (cursor.y < window.maxY() - 1) cursor.y++; + else { + screen->scrollUp(); + screen->scrollUp(); + } + } + action rlinefeed { + // this is documented as being vt52++ + // however the 2BSD termcap entry (dating to 1980) + // and every termcap since claims 50h supports it... + if (cursor.y) cursor.y--; + else { + screen->scrollDown(); + screen->scrollDown(); + } + } + + action erase_eos { + + iRect tmp; + + tmp.origin = cursor; + tmp.size = iSize( window.maxX() - cursor.x, 1); + normalize(tmp); + screen->eraseRect(tmp); + + tmp.origin = iPoint(0, cursor.y+1); + tmp.size = iSize(window.maxX(), window.maxY() - cursor.y - 1); + normalize(tmp); + screen->eraseRect(tmp); + + } + + + action erase_eol { + + iRect tmp; + + tmp.origin = cursor; + tmp.size = iSize( window.maxX() - cursor.x, 1); + normalize(tmp); + screen->eraseRect(tmp); + + } + + action identify { + // NB -- these indicate no copier. + switch(_model) { + case ModelVT50: + output->write(ESC "/A"); + break; + case ModelVT50H: + output->write(ESC "/H"); + break; + } + } + + action dca { + + unsigned y = _scratch[0]; + if (y >= window.maxY()) y = window.maxY() -1; + cursor.y = y; + + unsigned x = _scratch[1]; + if (x >= window.maxX()) x = window.maxX() -1; + cursor.x = x; + } + + + action forward { + if (cursor.x > window.maxX()-1) { + cursor.x = window.minX(); + if (cursor.y >= window.maxY()-1) { + screen->scrollUp(); + screen->scrollUp(); + } else { + cursor.y++; + } + } + + } + + arg1 = any ${ _scratch[0] = ((fc & 0x7f) - 32); }; + arg2 = any ${ _scratch[1] = ((fc & 0x7f) - 32); }; + + + control_codes = ( + 0x07 ${ NSBeep(); } + | 0x08 ${ if (cursor.x) cursor.x--; } + | 0x09 $tab + | 0x0a $linefeed + | 0x0d ${ cursor.x = 0; } + | 0x0e when vt50 arg1 arg2 $dca + ); + + escape_codes = ( + control_codes + | esc + | 'A' ${ if (cursor.y) cursor.y--; } + | 'B' when vt50h ${ if (cursor.y < window.maxY() -1) cursor.y++; } + | 'C' ${ if (cursor.x < window.maxX() -1) cursor.x++; } + | 'D' when vt50h ${ if (cursor.x) cursor.x--; } + #| 'F' when vt52_or_better ${ _graphics = true; } + #| 'G' when vt52_or_better ${ _graphics = false; } + | 'H' ${ cursor = iPoint(0, 0); } + | 'I' when vt50h $rlinefeed + | 'J' $erase_eos + | 'K' $erase_eol + | 'Y' when vt50h arg1 arg2 $dca + | 'Z' $identify + #| '=' when vt52_or_better ${ _altKeyPad = true; } + #| '>' when vt52_or_better ${ _altKeyPad = false; } + | any + ); + + main := ( + control_codes + | esc escape_codes + | 0x20 .. 0x7e $forward ${ + uint8_t c = fc; + if (c & 0x40) c &= ~0x20; + screen->putc(c, iPoint(cursor.x, cursor.y << 1), 0); + cursor.x++; + } + | any + )** $err { fgoto main; }; + + write data; +}%% + +@implementation VT50x + + +-(BOOL)resizable +{ + return NO; +} + +-(struct winsize)defaultSize +{ + struct winsize ws = { 0, 0, 0, 0}; + + // VT50x have 12 rows. They are double spaced. + + ws.ws_row = 12; + ws.ws_col = 80; + + return ws; + +} + +-(struct winsize)displaySize +{ + struct winsize ws = { 0, 0, 0, 0}; + + // VT50x have 12 rows. They are double spaced. + + ws.ws_row = 24; + ws.ws_col = 80; + + return ws; + +} + + ++(NSString *)name { + return @""; +} + +-(NSString *)name { + return @""; +} + +-(const char *)termName { + return ""; +} + + +-(void)reset: (BOOL)hard +{ + %% write init; + + _altKeyPad = false; + _graphics = false; + + if (hard) { + _context.cursor = iPoint(0,0); + _context.window = iRect(0, 0, 80, 12); + } + _context.flags = 0; +} + +-(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]; + uint8_t c; + + + if (flags & NSNumericPadKeyMask) + { + if (_altKeyPad) + { + const char *str = NULL; + switch (uc) + { + case '0': + str = ESC "?p"; + break; + case '1': + str = ESC "?q"; + break; + case '2': + str = ESC "?r"; + break; + case '3': + str = ESC "?s"; + break; + case '4': + str = ESC "?t"; + break; + case '5': + str = ESC "?u"; + break; + case '6': + str = ESC "?v"; + break; + case '7': + str = ESC "?w"; + break; + case '8': + str = ESC "?x"; + break; + case '9': + str = ESC "?y"; + break; + case '.': + str = ESC "?n"; + break; + case NSNewlineCharacter: //? + case NSEnterCharacter: + str = ESC "?M"; + break; + + } + if (str) + { + output->write(str); + break; + } + } + } + + + switch (uc) + { + case NSEnterCharacter: + output->write('\r'); + break; + case NSDeleteCharacter: + output->write(0x7f); + break; + case NSUpArrowFunctionKey: + output->write(ESC "A"); + break; + case NSDownArrowFunctionKey: + output->write(ESC "B"); + break; + case NSRightArrowFunctionKey: + output->write(ESC "C"); + break; + case NSLeftArrowFunctionKey: + output->write(ESC "D"); + break; + + // 3 function keys. (VT50H / VT52) + case NSF1FunctionKey: + output->write(ESC "P"); + break; + + case NSF2FunctionKey: + output->write(ESC "Q"); + break; + + case NSF3FunctionKey: + output->write(ESC "R"); + break; + + + default: + if (uc > 0x7f) break; + c = uc; + + if (flags & (NSShiftKeyMask | NSAlphaShiftKeyMask)) + { + c = toupper(c); + } + + if (flags & NSControlKeyMask) + { + c = CTRL(c); + } + output->write(c); + break; + } + } +} + +-(void)processData: (uint8_t *)data length: (size_t)length screen:(Screen *)screen output:(OutputChannel *)output +{ + std::transform(data, data + length, data, [](uint8_t c){ return c & 0x7f; }); + + const uint8_t *eof = nullptr; + const uint8_t *p = data; + const uint8_t *pe = std::copy_if(data, data + length, data, [](uint8_t c){ + if (c == 0 || c == 0x7f) return false; + return true; + }); + + iPoint &cursor = _context.cursor; + const iRect &window = _context.window; + + %%write exec; + + auto cc = cursor; + cc.y <<= 1; + if (cc.x == 80) cc.x = 79; + screen->setCursor(cc); +} + + +@end + +@implementation VT50 + ++(void)load { + [EmulatorManager registerClass: self]; +} + ++(NSString *)name { + return @"VT50"; +} +-(NSString *)name { + return @"VT50"; +} + +-(const char *)termName { + return "vt50"; +} + +-(id)init { + if ((self = [super init])) { + _model = ModelVT50; + [self reset: YES]; + + } + return self; +} + +@end + + +@implementation VT50H + ++(void)load { + [EmulatorManager registerClass: self]; +} + ++(NSString *)name { + return @"VT50H"; +} +-(NSString *)name { + return @"VT50H"; +} + +-(const char *)termName { + return "vt50h"; +} + +-(id)init { + if ((self = [super init])) { + _model = ModelVT50H; + [self reset: YES]; + + } + return self; +} + +@end diff --git a/Emulators/VT52.h b/Emulators/VT52.h index e72c643..3ca4ffb 100644 --- a/Emulators/VT52.h +++ b/Emulators/VT52.h @@ -28,11 +28,14 @@ @interface VT52 : VT5x @end +/* @interface VT50H : VT5x @end + @interface VT50 : VT5x @end +*/ @interface VT55 : VT5x @end diff --git a/Emulators/VT52.mm b/Emulators/VT52.mm index d70f653..207f365 100644 --- a/Emulators/VT52.mm +++ b/Emulators/VT52.mm @@ -43,7 +43,7 @@ enum { - +#if 0 @implementation VT50 +(void)load { @@ -119,6 +119,8 @@ enum { @end +#endif + @implementation VT52 +(void)load { diff --git a/TwoTerm.xcodeproj/project.pbxproj b/TwoTerm.xcodeproj/project.pbxproj index 27fa3f4..a5d8b72 100644 --- a/TwoTerm.xcodeproj/project.pbxproj +++ b/TwoTerm.xcodeproj/project.pbxproj @@ -43,6 +43,7 @@ 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 */; }; + B683F7102049E7B000470B99 /* VT50.mm.ragel in Sources */ = {isa = PBXBuildFile; fileRef = B683F70F2049E7B000470B99 /* VT50.mm.ragel */; }; B68E632A12FF909D00EAFF5F /* ExampleView.m in Sources */ = {isa = PBXBuildFile; fileRef = B68E632912FF909C00EAFF5F /* ExampleView.m */; }; B69D0FBA202799B10073CCB7 /* TermConfig.xib in Resources */ = {isa = PBXBuildFile; fileRef = B69D0FB8202799B10073CCB7 /* TermConfig.xib */; }; B69E32A920221C9E0086D7B1 /* ChildMonitor.mm in Sources */ = {isa = PBXBuildFile; fileRef = B69E32A820221C9E0086D7B1 /* ChildMonitor.mm */; }; @@ -176,6 +177,9 @@ B67B3CE312B6FA040033AE07 /* a2-charset-40.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "a2-charset-40.png"; sourceTree = ""; }; B67B3CE412B6FA040033AE07 /* a2-charset-80.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "a2-charset-80.png"; sourceTree = ""; }; B6801BD812EB549300B22E9E /* vt100-charset.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "vt100-charset.png"; sourceTree = ""; }; + B683F70E2049E7B000470B99 /* VT50.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = VT50.h; sourceTree = ""; }; + B683F70F2049E7B000470B99 /* VT50.mm.ragel */ = {isa = PBXFileReference; lastKnownFileType = text; path = VT50.mm.ragel; sourceTree = ""; }; + B683F711204AE32900470B99 /* VT05.mm.ragel */ = {isa = PBXFileReference; lastKnownFileType = text; path = VT05.mm.ragel; sourceTree = ""; }; B68E632812FF909C00EAFF5F /* ExampleView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ExampleView.h; sourceTree = ""; }; B68E632912FF909C00EAFF5F /* ExampleView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ExampleView.m; sourceTree = ""; }; B69D0FB9202799B10073CCB7 /* English */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = English; path = English.lproj/TermConfig.xib; sourceTree = ""; }; @@ -367,10 +371,13 @@ B612F45D12DD5DF1005D1B77 /* PTSE.mm.ragel */, B612F45E12DD5DF1005D1B77 /* VT05.h */, B612F45F12DD5DF1005D1B77 /* VT05.mm */, + B683F711204AE32900470B99 /* VT05.mm.ragel */, B612F46012DD5DF1005D1B77 /* VT100.h */, B612F46112DD5DF1005D1B77 /* VT100.mm */, B612F46212DD5DF1005D1B77 /* VT52.h */, B612F46312DD5DF1005D1B77 /* VT52.mm */, + B683F70E2049E7B000470B99 /* VT50.h */, + B683F70F2049E7B000470B99 /* VT50.mm.ragel */, ); path = Emulators; sourceTree = ""; @@ -545,6 +552,7 @@ B675F4AC1E56A7F2004B0D9C /* GSOSConsole.mm.ragel in Sources */, B6D1CD071E577E7D00C4A6BC /* PTSE.mm.ragel in Sources */, B6C21CD62033580200671774 /* RolloverButton.m in Sources */, + B683F7102049E7B000470B99 /* VT50.mm.ragel in Sources */, B69E32A920221C9E0086D7B1 /* ChildMonitor.mm in Sources */, B6407805201CE93500D3F2D1 /* GNOConsole.mm.ragel in Sources */, 8D11072D0486CEB800E47090 /* main.m in Sources */,