rewrite VT05 / VT50 / VT50H with ragel.

This commit is contained in:
Kelvin Sherlock 2018-03-03 10:23:57 -05:00
parent 020b095542
commit 99f5d60be8
7 changed files with 732 additions and 4 deletions

View File

@ -15,11 +15,10 @@
@interface VT05 : NSObject <Emulator> {
unsigned _state;
unsigned cs;
unsigned _scratch[2];
context _context;
BOOL _upperCase;
}
-(void)tab: (Screen *)screen;
@end

236
Emulators/VT05.mm.ragel Normal file
View File

@ -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 <sys/ttydefaults.h>
#include <cctype>
#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

33
Emulators/VT50.h Normal file
View File

@ -0,0 +1,33 @@
//
// VT50.h
// TwoTerm
//
// Created by Kelvin Sherlock on 3/2/2018.
//
#import <Foundation/Foundation.h>
#import <Cocoa/Cocoa.h>
#import "Emulator.h"
#include "iGeometry.h"
#include "Screen.h"
@interface VT50x : NSObject <Emulator> {
unsigned cs;
int _scratch[2];
context _context;
unsigned _model;
BOOL _altKeyPad;
BOOL _graphics;
}
@end
@interface VT50 : VT50x
@end
@interface VT50H : VT50x
@end

447
Emulators/VT50.mm.ragel Normal file
View File

@ -0,0 +1,447 @@
//
// VT50.m
// TwoTerm
//
// Created by Kelvin Sherlock on 3/2/2018.
//
#include <sys/ttydefaults.h>
#include <cctype>
#include <cstdio>
#include <numeric>
#include <algorithm>
#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

View File

@ -28,11 +28,14 @@
@interface VT52 : VT5x
@end
/*
@interface VT50H : VT5x
@end
@interface VT50 : VT5x
@end
*/
@interface VT55 : VT5x
@end

View File

@ -43,7 +43,7 @@ enum {
#if 0
@implementation VT50
+(void)load {
@ -119,6 +119,8 @@ enum {
@end
#endif
@implementation VT52
+(void)load {

View File

@ -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 = "<group>"; };
B67B3CE412B6FA040033AE07 /* a2-charset-80.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "a2-charset-80.png"; sourceTree = "<group>"; };
B6801BD812EB549300B22E9E /* vt100-charset.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "vt100-charset.png"; sourceTree = "<group>"; };
B683F70E2049E7B000470B99 /* VT50.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = VT50.h; sourceTree = "<group>"; };
B683F70F2049E7B000470B99 /* VT50.mm.ragel */ = {isa = PBXFileReference; lastKnownFileType = text; path = VT50.mm.ragel; sourceTree = "<group>"; };
B683F711204AE32900470B99 /* VT05.mm.ragel */ = {isa = PBXFileReference; lastKnownFileType = text; path = VT05.mm.ragel; sourceTree = "<group>"; };
B68E632812FF909C00EAFF5F /* ExampleView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ExampleView.h; sourceTree = "<group>"; };
B68E632912FF909C00EAFF5F /* ExampleView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ExampleView.m; sourceTree = "<group>"; };
B69D0FB9202799B10073CCB7 /* English */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = English; path = English.lproj/TermConfig.xib; sourceTree = "<group>"; };
@ -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 = "<group>";
@ -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 */,