rewrite GS/OS Console.

git-svn-id: svn://qnap.local/TwoTerm/branches/fix-gno-scrolling-region@3169 5590a31f-7b70-45f8-8c82-aa3a8e5f4507
This commit is contained in:
Kelvin Sherlock 2017-02-17 03:54:52 +00:00
parent 8a5373f906
commit cef619bae7
6 changed files with 350 additions and 142 deletions

View File

@ -37,6 +37,7 @@
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 */; };
@ -116,7 +117,7 @@
B612F45812DD5DF1005D1B77 /* Emulator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Emulator.h; sourceTree = "<group>"; };
B612F45912DD5DF1005D1B77 /* EmulatorManager.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = EmulatorManager.mm; sourceTree = "<group>"; };
B612F45A12DD5DF1005D1B77 /* GNOConsole.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GNOConsole.h; sourceTree = "<group>"; };
B612F45B12DD5DF1005D1B77 /* GNOConsole.mm.ragel */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = GNOConsole.mm.ragel; sourceTree = "<group>"; };
B612F45B12DD5DF1005D1B77 /* GNOConsole.mm.ragel */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.objcpp; fileEncoding = 4; path = GNOConsole.mm.ragel; sourceTree = "<group>"; };
B612F45C12DD5DF1005D1B77 /* PTSE.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PTSE.h; sourceTree = "<group>"; };
B612F45D12DD5DF1005D1B77 /* PTSE.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = PTSE.mm; sourceTree = "<group>"; };
B612F45E12DD5DF1005D1B77 /* VT05.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = VT05.h; sourceTree = "<group>"; };
@ -438,6 +439,7 @@
files = (
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 */,

View File

@ -116,7 +116,6 @@
*/
%%{
machine console;
alphtype unsigned int;
@ -164,8 +163,8 @@
_scratch[2] = std::max(0, _scratch[2]);
_scratch[1] = std::min(80, _scratch[1]+1);
_scratch[3] = std::min(24, _scratch[3]+1);
_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;
@ -385,6 +384,7 @@
write data;
}%%
@implementation GNOConsole

View File

@ -12,19 +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 <Emulator> {
gsos_context _context;
std::vector<gsos_context> _context_stack;
unsigned cs;
TextPort _textPort;
iRect _window;
std::vector<TextPort> _tpStack;
int _scratch[4];
int _cursorType;
bool _consLF;
bool _consDLE;
}
@end

View File

@ -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

View File

@ -89,24 +89,30 @@ void Screen::putc(uint8_t c, iPoint cursor, uint8_t flags)
#pragma mark -
#pragma mark Erase
void Screen::eraseScreen()
{
void Screen::eraseScreen() { fillScreen(char_info()); }
void Screen::eraseRect(iRect rect) { fillRect(rect, char_info()); }
void Screen::fillScreen(char_info ci) {
for (auto &line : _screen) {
std::fill(line.begin(), line.end(), char_info());
std::fill(line.begin(), line.end(), ci);
}
_updates.push_back(iPoint(0,0));
_updates.push_back(iPoint(width() - 1, height() - 1));
}
void Screen::eraseRect(iRect rect) {
void Screen::fillRect(iRect rect, char_info ci) {
rect = rect.intersection(_frame);
if (!rect.valid()) return;
if (rect == _frame) return eraseScreen();
if (rect == _frame) return fillScreen(ci);
auto yIter = _screen.begin() + rect.origin.y;
auto yEnd = yIter + rect.size.height;
@ -116,7 +122,7 @@ void Screen::eraseRect(iRect rect) {
auto xIter = line.begin() + rect.origin.x;
auto xEnd = xIter + rect.size.width;
std::fill(xIter, xEnd, char_info());
std::fill(xIter, xEnd, ci);
}
_updates.push_back(rect.origin);
_updates.push_back(iPoint(rect.maxX()-1, rect.maxY()-1));

View File

@ -101,6 +101,9 @@ public:
void eraseScreen();
void eraseRect(iRect rect);
void fillScreen(char_info ci);
void fillRect(iRect rect, char_info ci);
void scrollUp();
void scrollUp(iRect window);
@ -187,6 +190,9 @@ inline int Screen::width() const
inline void Screen::setCursor(iPoint point)
{
if (point.x >= _frame.width()) point.x = _frame.width() - 1;
if (point.y >= _frame.height()) point.y = _frame.height() - 1;
_cursor = point;
}