Synchronet now requires the libarchive development package (e.g. libarchive-dev on Debian-based Linux distros, libarchive.org for more info) to build successfully.

Commit be7d06bc authored by rswindell's avatar rswindell

External programs that are "binary" in nature (e.g. file transfer protocols)

now have their output translated to PETSCII equivalents for PETSCII terminals
(but input is not yet translated).
.seq files are now sent untranslated via printfile() and putmsg() to PETSCII
terminals (using the new P_PETSCII putmsg mode flag).
.seq files (and P_PETSCII mode text printed via putmsg) is now converted
(poorly) from PETSCII to CP437 - this is still a work-in-progress.
Remove the remants of WIP and HTMLterm support from putmsg() and printfile().
parent 30c33127
......@@ -84,8 +84,8 @@ int sbbs_t::bputs(const char *str)
return(l);
}
/* Perform PETSCII terminal output translation */
static unsigned char petscii(unsigned char ch)
/* Perform PETSCII terminal output translation (from ASCII/CP437) */
unsigned char cp437_to_petscii(unsigned char ch)
{
if(isalpha(ch))
return ch ^ 0x20; /* swap upper/lower case */
......@@ -151,13 +151,92 @@ static unsigned char petscii(unsigned char ch)
case 207:
case 208:
case 193: return '\xB1';
}
if(ch&0x80)
return exascii_to_ascii_char(ch);
return ch;
}
/* Perform PETSCII conversion to ANSI-BBS/CP437 */
int sbbs_t::petscii_to_ansibbs(unsigned char ch)
{
if((ch&0xe0) == 0xc0) /* "Codes $60-$7F are, actually, copies of codes $C0-$DF" */
ch = 0x60 | (ch&0x1f);
if(isalpha(ch))
return outchar(ch ^ 0x20); /* swap upper/lower case */
switch(ch) {
case '\r': newline(); break;
case PETSCII_HOME: cursor_home(); break;
case PETSCII_CLEAR: return CLS;
case PETSCII_DELETE: backspace(); break;
case PETSCII_LEFT: cursor_left(); break;
case PETSCII_RIGHT: cursor_right(); break;
case PETSCII_UP: cursor_up(); break;
case PETSCII_DOWN: cursor_down(); break;
case PETSCII_BRITPOUND: return outchar((char)156);
case PETSCII_CHECKMARK: return outchar((char)251);
case PETSCII_CHECKERBRD:
case PETSCII_LIGHTHASH: return outchar((char)176);
case 0x7e:
case PETSCII_MEDIUMHASH: return outchar((char)177);
case PETSCII_HEAVYHASH: return outchar((char)178);
case PETSCII_SOLID: return outchar((char)219);
case PETSCII_BOTTOMHALF: return outchar((char)220);
case PETSCII_LEFTHALF: return outchar((char)221);
case PETSCII_RIGHTHALF: return outchar((char)222);
case PETSCII_TOPHALF: return outchar((char)223);
case PETSCII_LWRLFTBOX:
case PETSCII_LWRRHTBOX:
case PETSCII_UPRRHTBOX:
case PETSCII_UPRLFTBOX: return outchar((char)254);
/* Line drawing chars */
case 0x7D:
case PETSCII_VERTLINE: return outchar((char)179);
case PETSCII_HORZLINE: return outchar((char)196);
case 0x7B:
case PETSCII_CROSS: return outchar((char)197);
case (uchar)'\xBD': return outchar((char)217);
case (uchar)'\xB0': return outchar((char)218);
case (uchar)'\xAE': return outchar((char)191);
case (uchar)'\xAD': return outchar((char)192);
case (uchar)'\xAB': return outchar((char)195);
case (uchar)'\xB3': return outchar((char)180);
case (uchar)'\xB2': return outchar((char)194);
case (uchar)'\xB1': return outchar((char)193);
case PETSCII_BLACK: return attr(BLACK);
case PETSCII_WHITE: return attr(WHITE);
case PETSCII_RED: return attr(RED);
case PETSCII_GREEN: return attr(GREEN);
case PETSCII_BLUE: return attr(BLUE);
case PETSCII_ORANGE: return attr(MAGENTA);
case PETSCII_BROWN: return attr(BROWN);
case PETSCII_YELLOW: return attr(YELLOW);
case PETSCII_CYAN: return attr(LIGHTCYAN);
case PETSCII_LIGHTRED: return attr(LIGHTRED);
case PETSCII_DARKGRAY: return attr(DARKGRAY);
case PETSCII_MEDIUMGRAY: return attr(CYAN);
case PETSCII_LIGHTGREEN: return attr(LIGHTGREEN);
case PETSCII_LIGHTBLUE: return attr(LIGHTBLUE);
case PETSCII_LIGHTGRAY: return attr(LIGHTGRAY);
case PETSCII_PURPLE: return attr(LIGHTMAGENTA);
case PETSCII_REVERSE_ON: return attr((curatr&0x07) << 4);
case PETSCII_REVERSE_OFF: return attr(curatr >> 4);
case PETSCII_FLASH_ON: return attr(curatr | BLINK);
case PETSCII_FLASH_OFF: return attr(curatr & ~BLINK);
default:
if(ch&0x80) return bprintf("#%3d", ch);
return outchar(ch);
case PETSCII_UPPERLOWER:
case PETSCII_UPPERGRFX:
/* Do nothing */
return 0;
}
return 0;
}
/****************************************************************************/
/* Raw put string (remotely) */
/* Performs Telnet IAC escaping */
......@@ -176,9 +255,9 @@ int sbbs_t::rputs(const char *str, size_t len)
for(l=0;l<len && online;l++) {
if(str[l]==(char)TELNET_IAC && !(telnet_mode&TELNET_MODE_OFF))
outcom(TELNET_IAC); /* Must escape Telnet IAC char (255) */
char ch = str[l];
uchar ch = str[l];
if(term&PETSCII)
ch = petscii(ch);
ch = cp437_to_petscii(ch);
if(outcom(ch)!=0)
break;
if(lbuflen<LINE_BUFSIZE)
......@@ -257,7 +336,7 @@ long sbbs_t::term_supports(long cmp_flags)
/* Performs column counting, line counting, and auto-pausing */
/* Performs saveline buffering (for restoreline) */
/****************************************************************************/
void sbbs_t::outchar(char ch)
int sbbs_t::outchar(char ch)
{
/*
* outchar_esc values:
......@@ -271,7 +350,7 @@ void sbbs_t::outchar(char ch)
*/
if(console&CON_ECHO_OFF)
return;
return 0;
if(ch==ESC && outchar_esc < 4)
outchar_esc=1;
else if(outchar_esc==1) {
......@@ -349,7 +428,7 @@ void sbbs_t::outchar(char ch)
if(ch==(char)TELNET_IAC && !(telnet_mode&TELNET_MODE_OFF))
outcom(TELNET_IAC); /* Must escape Telnet IAC char (255) */
if(term&PETSCII) {
char pet = petscii(ch);
uchar pet = cp437_to_petscii(ch);
if(pet == PETSCII_SOLID)
outcom(PETSCII_REVERSE_ON);
outcom(pet);
......@@ -404,6 +483,7 @@ void sbbs_t::outchar(char ch)
lncntr=0;
pause();
}
return 0;
}
void sbbs_t::center(char *instr)
......@@ -803,7 +883,7 @@ void sbbs_t::ctrl_a(char x)
/****************************************************************************/
/* Sends terminal control codes to change remote terminal colors/attributes */
/****************************************************************************/
void sbbs_t::attr(int atr)
int sbbs_t::attr(int atr)
{
char str[16];
int newatr = atr;
......@@ -873,6 +953,7 @@ void sbbs_t::attr(int atr)
else if(term&ANSI)
rputs(ansi(newatr,curatr,str));
curatr=newatr;
return 0;
}
/****************************************************************************/
......
......@@ -226,12 +226,12 @@ int sbbs_t::protocol(prot_t* prot, enum XFER_TYPE type
/* enable telnet binary transmission in both directions */
request_telnet_opt(TELNET_DO,TELNET_BINARY_TX);
request_telnet_opt(TELNET_WILL,TELNET_BINARY_TX);
ex_mode=0;
ex_mode = EX_BIN;
if(prot->misc&PROT_NATIVE)
ex_mode|=EX_NATIVE;
#ifdef __unix__ /* file xfer progs must use stdio on Unix */
if(!(prot->misc&PROT_SOCKET))
ex_mode|=(EX_STDIO|EX_BIN);
ex_mode|=EX_STDIO;
#endif
i=external(cmdline,ex_mode,p);
......
......@@ -43,32 +43,29 @@
/* for pauses, aborts and ANSI. 'str' is the path of the file to print */
/* Called from functions menu and text_sec */
/****************************************************************************/
bool sbbs_t::printfile(char *str, long mode)
bool sbbs_t::printfile(const char* fname, long mode)
{
char* buf;
char fpath[MAX_PATH+1];
char* p;
int file;
BOOL wip=FALSE,rip=FALSE,html=FALSE;
BOOL rip=FALSE;
long l,length,savcon=console;
FILE *stream;
p=strrchr(str,'.');
SAFECOPY(fpath, fname);
fexistcase(fpath);
p=getfext(fpath);
if(p!=NULL) {
if(stricmp(p,".wip")==0) {
wip=TRUE;
mode|=P_NOPAUSE;
}
else if(stricmp(p,".rip")==0) {
if(stricmp(p,".rip")==0) {
rip=TRUE;
mode|=P_NOPAUSE;
}
else if(stricmp(p,".html")==0) {
html=TRUE;
mode|=(P_HTML|P_NOPAUSE);
} else if(stricmp(p, ".seq") == 0) {
mode |= P_PETSCII;
}
}
if(mode&P_NOABORT || wip || rip || html) {
if(mode&P_NOABORT || rip) {
if(online==ON_REMOTE && console&CON_R_ECHO) {
rioctl(IOCM|ABORT);
rioctl(IOCS|ABORT);
......@@ -76,17 +73,16 @@ bool sbbs_t::printfile(char *str, long mode)
sys_status&=~SS_ABORT;
}
if(!(mode&P_NOCRLF) && !tos && !wip && !rip && !html) {
if(!(mode&P_NOCRLF) && !tos && !rip) {
CRLF;
}
fexistcase(str);
if((stream=fnopen(&file,str,O_RDONLY|O_DENYNONE))==NULL) {
if((stream=fnopen(&file,fpath,O_RDONLY|O_DENYNONE))==NULL) {
if(!(mode&P_NOERROR)) {
lprintf(LOG_NOTICE,"!Error %d (%s) opening: %s"
,errno,strerror(errno),str);
,errno,strerror(errno),fpath);
bputs(text[FileNotFound]);
if(SYSOP) bputs(str);
if(SYSOP) bputs(fpath);
CRLF;
}
return false;
......@@ -95,25 +91,25 @@ bool sbbs_t::printfile(char *str, long mode)
length=(long)filelength(file);
if(length<0) {
fclose(stream);
errormsg(WHERE,ERR_CHK,str,length);
errormsg(WHERE,ERR_CHK,fpath,length);
return false;
}
if((buf=(char*)malloc(length+1L))==NULL) {
fclose(stream);
errormsg(WHERE,ERR_ALLOC,str,length+1L);
errormsg(WHERE,ERR_ALLOC,fpath,length+1L);
return false;
}
l=lread(file,buf,length);
fclose(stream);
if(l!=length)
errormsg(WHERE,ERR_READ,str,length);
errormsg(WHERE,ERR_READ,fpath,length);
else {
buf[l]=0;
putmsg(buf,mode);
}
free(buf);
if((mode&P_NOABORT || wip || rip || html) && online==ON_REMOTE) {
if((mode&P_NOABORT || rip) && online==ON_REMOTE) {
SYNC;
rioctl(IOSM|ABORT);
}
......@@ -123,13 +119,16 @@ bool sbbs_t::printfile(char *str, long mode)
return true;
}
bool sbbs_t::printtail(char *str, int lines, long mode)
bool sbbs_t::printtail(const char* fname, int lines, long mode)
{
char* buf;
char fpath[MAX_PATH+1];
char* p;
int file,cur=0;
long length,l;
SAFECOPY(fpath, fname);
fexistcase(fpath);
if(mode&P_NOABORT) {
if(online==ON_REMOTE) {
rioctl(IOCM|ABORT);
......@@ -140,13 +139,12 @@ bool sbbs_t::printtail(char *str, int lines, long mode)
if(!tos) {
CRLF;
}
fexistcase(str);
if((file=nopen(str,O_RDONLY|O_DENYNONE))==-1) {
if((file=nopen(fpath,O_RDONLY|O_DENYNONE))==-1) {
if(!(mode&P_NOERROR)) {
lprintf(LOG_NOTICE,"!Error %d (%s) opening: %s"
,errno,strerror(errno),str);
,errno,strerror(errno),fpath);
bputs(text[FileNotFound]);
if(SYSOP) bputs(str);
if(SYSOP) bputs(fpath);
CRLF;
}
return false;
......@@ -154,18 +152,18 @@ bool sbbs_t::printtail(char *str, int lines, long mode)
length=(long)filelength(file);
if(length<0) {
close(file);
errormsg(WHERE,ERR_CHK,str,length);
errormsg(WHERE,ERR_CHK,fpath,length);
return false;
}
if((buf=(char*)malloc(length+1L))==NULL) {
close(file);
errormsg(WHERE,ERR_ALLOC,str,length+1L);
errormsg(WHERE,ERR_ALLOC,fpath,length+1L);
return false;
}
l=lread(file,buf,length);
close(file);
if(l!=length)
errormsg(WHERE,ERR_READ,str,length);
errormsg(WHERE,ERR_READ,fpath,length);
else {
buf[l]=0;
p=(buf+l)-1;
......
......@@ -64,18 +64,17 @@ char sbbs_t::putmsg(const char *buf, long mode)
attr(LIGHTGRAY);
if(mode&P_NOPAUSE)
sys_status|=SS_PAUSEOFF;
if(mode&P_HTML)
putcom("\x02\x02");
long term = term_supports();
if(!(mode&P_NOATCODES) && memcmp(str, "@WRAPOFF@", 9) == 0) {
mode &= ~P_WORDWRAP;
l += 9;
}
if(mode&P_WORDWRAP) {
char* term = NULL;
char* wrapoff = NULL;
if(!(mode&P_NOATCODES)) {
term = strstr((char*)str+l, "@WRAPOFF@");
if(term != NULL)
*term = 0;
wrapoff = strstr((char*)str+l, "@WRAPOFF@");
if(wrapoff != NULL)
*wrapoff = 0;
}
char *wrapped;
if((wrapped=::wordwrap((char*)str+l, cols-1, 79, /* handle_quotes: */TRUE)) == NULL)
......@@ -85,7 +84,7 @@ char sbbs_t::putmsg(const char *buf, long mode)
putmsg(wrapped, mode&(~P_WORDWRAP));
free(wrapped);
l=strlen(str);
if(term != NULL)
if(wrapoff != NULL)
l += 9; // Skip "<NUL>WRAPOFF@"
}
}
......@@ -143,7 +142,7 @@ char sbbs_t::putmsg(const char *buf, long mode)
l+=4;
}
else if(cfg.sys_misc&SM_RENEGADE && str[l]=='|' && isdigit((unsigned char)str[l+1])
&& isdigit((unsigned char)str[l+2]) && !(useron.misc&(RIP|WIP))) {
&& isdigit((unsigned char)str[l+2]) && !(useron.misc&RIP)) {
sprintf(tmp2,"%.2s",str+l+1);
i=atoi(tmp2);
if(i>=16) { /* setting background */
......@@ -158,7 +157,7 @@ char sbbs_t::putmsg(const char *buf, long mode)
l+=3; /* Skip |xx */
}
else if(cfg.sys_misc&SM_CELERITY && str[l]=='|' && isalpha((unsigned char)str[l+1])
&& !(useron.misc&(RIP|WIP))) {
&& !(useron.misc&RIP)) {
switch(str[l+1]) {
case 'k':
attr((curatr&0xf0)|BLACK);
......@@ -270,10 +269,8 @@ char sbbs_t::putmsg(const char *buf, long mode)
continue;
}
}
if(str[l]=='!' && str[l+1]=='|' && useron.misc&(RIP|WIP)) /* RIP */
if(str[l]=='!' && str[l+1]=='|' && useron.misc&RIP) /* RIP */
lncntr=0; /* so defeat pause */
if(str[l]==ESC && str[l+1]=='$') /* WIP command */
lncntr=0;
if(str[l]=='@' && !(mode&P_NOATCODES)) {
if(memcmp(str+l, "@EOF@", 5) == 0)
break;
......@@ -310,19 +307,6 @@ char sbbs_t::putmsg(const char *buf, long mode)
break;
}
/* In HTML mode, defer PAUSE and MORE to end and suppress message */
if(mode&P_HTML) {
if(!memcmp(str+l,"@MORE@",6)) {
defered_pause=TRUE;
l+=6;
continue;
}
if(!memcmp(str+l,"@PAUSE@",7)) {
defered_pause=TRUE;
l+=7;
continue;
}
}
i=show_atcode((char *)str+l); /* returns 0 if not valid @ code */
l+=i; /* i is length of code string */
if((sys_status&SS_ABORT) && !lines_printed) /* Aborted at (auto) pause prompt (e.g. due to CLS)? */
......@@ -332,18 +316,16 @@ char sbbs_t::putmsg(const char *buf, long mode)
}
if(mode&P_CPM_EOF && str[l]==CTRL_Z)
break;
outchar(str[l]);
#if 0
if(!(mode&P_HTML) && !exatr && !outchar_esc && lncntr && lbuflen && cols && ++col==cols)
lncntr++;
if(mode&P_PETSCII) {
if(term&PETSCII)
outcom(str[l]);
else
col=0;
#endif
petscii_to_ansibbs(str[l]);
} else
outchar(str[l]);
l++;
}
}
if(mode&P_HTML)
putcom("\x02");
if(!(mode&P_SAVEATR)) {
console=orgcon;
attr(tmpatr);
......@@ -353,10 +335,7 @@ char sbbs_t::putmsg(const char *buf, long mode)
/* Handle defered pauses */
if(defered_pause) {
if(mode&P_HTML)
getkey(0);
else
pause();
pause();
}
ret=str[l];
......
......@@ -703,7 +703,7 @@ public:
#endif
;
void backspace(void); /* Output a destructive backspace via outchar */
void outchar(char ch); /* Output a char - check echo and emu. */
int outchar(char ch); /* Output a char - check echo and emu. */
void center(char *str);
void clearline(void);
void cleartoeol(void);
......@@ -721,6 +721,9 @@ public:
void progress(const char* str, int count, int total, int interval=1);
bool saveline(void);
bool restoreline(void);
int petscii_to_ansibbs(unsigned char);
int attr(int); /* Change text color/attributes */
void ctrl_a(char); /* Performs Ctrl-Ax attribute changes */
/* getstr.cpp */
size_t getstr_offset;
......@@ -744,8 +747,8 @@ public:
char handle_ctrlkey(char ch, long mode=0);
/* prntfile.cpp */
bool printfile(char *str, long mode);
bool printtail(char *str, int lines, long mode);
bool printfile(const char* fname, long mode);
bool printtail(const char* fname, int lines, long mode);
bool menu(const char *code, long mode = 0);
bool menu_exists(const char *code, const char* ext=NULL, char* realpath=NULL);
......@@ -755,8 +758,6 @@ public:
long mselect(const char *title, str_list_t list, unsigned max_selections, const char* item_fmt, const char* selected_str, const char* unselected_str, const char* prompt_fmt);
void redrwstr(char *strin, int i, int l, long mode);
void attr(int atr); /* Change local and remote text attributes */
void ctrl_a(char x); /* Peforms the Ctrl-Ax attribute changes */
/* atcodes.cpp */
int show_atcode(const char *code);
......@@ -1175,6 +1176,9 @@ extern "C" {
/* qwk.cpp */
DLLEXPORT int qwk_route(scfg_t*, const char *inaddr, char *fulladdr, size_t maxlen);
/* con_out.cpp */
unsigned char cp437_to_petscii(unsigned char);
#ifdef JAVASCRIPT
typedef struct {
......
......@@ -754,6 +754,7 @@ typedef enum { /* Values for xtrn_t.event */
#define P_CPM_EOF (1<<8) /* Ignore Ctrl-Z chars (CPM End-of-File) */
#define P_TRUNCATE (1<<9) /* Truncate (don't display) long lines */
#define P_NOERROR (1<<10) /* Don't report error if file doesn't exist */
#define P_PETSCII (1<<11) /* Message is native PETSCII */
/* Bits in 'mode' for listfiles */
#define FL_ULTIME (1<<0) /* List files by upload time */
......
......@@ -255,6 +255,13 @@ BYTE* telnet_expand(BYTE* inbuf, ulong inlen, BYTE* outbuf, ulong& newlen)
return(outbuf);
}
static void petscii_convert(BYTE* buf, ulong len)
{
for(ulong i=0; i<len; i++) {
buf[i] = cp437_to_petscii(buf[i]);
}
}
static bool native_executable(scfg_t* cfg, const char* cmdline, long mode)
{
char* p;
......@@ -929,6 +936,8 @@ int sbbs_t::external(const char* cmdline, long mode, const char* startup_dir)
lprintf(LOG_ERR,"output buffer overflow");
rd=RingBufFree(&outbuf);
}
if(!(mode&EX_BIN) && term_supports(PETSCII))
petscii_convert(bp, rd);
RingBufWrite(&outbuf, bp, rd);
}
} else { // Windows 9x
......@@ -996,6 +1005,8 @@ int sbbs_t::external(const char* cmdline, long mode, const char* startup_dir)
lprintf(LOG_ERR,"output buffer overflow");
rd=RingBufFree(&outbuf);
}
if(!(mode&EX_BIN) && term_supports(PETSCII))
petscii_convert(bp, rd);
RingBufWrite(&outbuf, bp, rd);
}
}
......@@ -1928,18 +1939,21 @@ int sbbs_t::external(const char* cmdline, long mode, const char* startup_dir)
}
else
bp=telnet_expand(buf, rd, output_buf, output_len);
} else if ((mode & EX_STDIO) != EX_STDIO) {
/* LF to CRLF expansion */
bp=lf_expand(buf, rd, output_buf, output_len);
} else if(mode&EX_WWIV) {
bp=wwiv_expand(buf, rd, wwiv_buf, output_len, useron.misc, wwiv_flag);
if(output_len > sizeof(wwiv_buf))
lprintf(LOG_ERR, "WWIV_BUF OVERRUN");
} else {
bp=buf;
output_len=rd;
if ((mode & EX_STDIO) != EX_STDIO) {
/* LF to CRLF expansion */
bp=lf_expand(buf, rd, output_buf, output_len);
} else if(mode&EX_WWIV) {
bp=wwiv_expand(buf, rd, wwiv_buf, output_len, useron.misc, wwiv_flag);
if(output_len > sizeof(wwiv_buf))
lprintf(LOG_ERR, "WWIV_BUF OVERRUN");
} else {
bp=buf;
output_len=rd;
}
if (term_supports(PETSCII))
petscii_convert(bp, output_len);
}
/* Did expansion overrun the output buffer? */
if(output_len>sizeof(output_buf)) {
lprintf(LOG_ERR,"OUTPUT_BUF OVERRUN");
......
......@@ -76,28 +76,28 @@ enum petscii_char {
/* Symbols (which don't align with ASCII) */
PETSCII_BRITPOUND = 92,
/* Graphic chars */
PETSCII_LIGHTHASH = '\xA6',
PETSCII_MEDIUMHASH = '\xDE',
PETSCII_HEAVYHASH = '\xA9',
PETSCII_SOLID = '\xA0', // Actually inversed solid (empty)
PETSCII_LEFTHALF = '\xA1',
PETSCII_RIGHTHALF = '\xB6', // Not quite a full half
PETSCII_TOPHALF = '\xB8', // Not quite a full half
PETSCII_BOTTOMHALF = '\xA2',
PETSCII_CHECKMARK = '\xBA',
PETSCII_CROSS = '\xDB',
PETSCII_HORZLINE = '\xC0',
PETSCII_VERTLINE = '\xDD',
PETSCII_LWRRHTBOX = '\xAC',
PETSCII_LWRLFTBOX = '\xBB',
PETSCII_UPRRHTBOX = '\xBC',
PETSCII_UPRLFTBOX = '\xBE',
PETSCII_CHECKERBRD = '\xBF',
PETSCII_LIGHTHASH = 0xA6,
PETSCII_MEDIUMHASH = 0xDE,
PETSCII_HEAVYHASH = 0xA9,
PETSCII_SOLID = 0xA0, // Actually inversed solid (empty)
PETSCII_LEFTHALF = 0xA1,
PETSCII_RIGHTHALF = 0xB6, // Not quite a full half
PETSCII_TOPHALF = 0xB8, // Not quite a full half
PETSCII_BOTTOMHALF = 0xA2,
PETSCII_CHECKMARK = 0xBA,
PETSCII_CROSS = 0xDB,
PETSCII_HORZLINE = 0xC0,
PETSCII_VERTLINE = 0xDD,
PETSCII_LWRRHTBOX = 0xAC,
PETSCII_LWRLFTBOX = 0xBB,
PETSCII_UPRRHTBOX = 0xBC,
PETSCII_UPRLFTBOX = 0xBE,
PETSCII_CHECKERBRD = 0xBF,
/* Replacement chars (missing ASCII chars) */
PETSCII_BACKSLASH = '/', // the 109 graphics char is an 'M' in shifted/text mode :-(
PETSCII_BACKTICK = '\xAD', // a graphics char actually
PETSCII_TILDE = '\xA8', // a graphics char actually
PETSCII_UNDERSCORE = '\xA4', // a graphics char actually