/*
|
/*
|
* term.c -- terminal simulation
|
* term.c -- terminal simulation
|
*/
|
*/
|
|
|
|
|
#ifdef __linux__
|
#ifdef __linux__
|
#define _XOPEN_SOURCE
|
#define _XOPEN_SOURCE
|
#endif
|
#endif
|
|
|
#include <stdio.h>
|
#include <stdio.h>
|
#include <stdlib.h>
|
#include <stdlib.h>
|
#include <string.h>
|
#include <string.h>
|
#include <setjmp.h>
|
#include <setjmp.h>
|
#include <signal.h>
|
#include <signal.h>
|
#include <sys/types.h>
|
#include <sys/types.h>
|
#include <sys/wait.h>
|
#include <sys/wait.h>
|
#include <unistd.h>
|
#include <unistd.h>
|
#include <fcntl.h>
|
#include <fcntl.h>
|
#include <termios.h>
|
#include <termios.h>
|
|
|
#include "common.h"
|
#include "common.h"
|
#include "console.h"
|
#include "console.h"
|
#include "error.h"
|
#include "error.h"
|
#include "except.h"
|
#include "except.h"
|
#include "cpu.h"
|
#include "cpu.h"
|
#include "timer.h"
|
#include "timer.h"
|
#include "term.h"
|
#include "term.h"
|
|
|
|
|
/**************************************************************/
|
/**************************************************************/
|
|
|
|
|
static Bool debug = false;
|
static Bool debug = false;
|
|
|
|
|
typedef struct {
|
typedef struct {
|
pid_t pid;
|
pid_t pid;
|
FILE *in;
|
FILE *in;
|
FILE *out;
|
FILE *out;
|
Word rcvrCtrl;
|
Word rcvrCtrl;
|
Word rcvrData;
|
Word rcvrData;
|
int rcvrIRQ;
|
int rcvrIRQ;
|
Word xmtrCtrl;
|
Word xmtrCtrl;
|
Word xmtrData;
|
Word xmtrData;
|
int xmtrIRQ;
|
int xmtrIRQ;
|
} Terminal;
|
} Terminal;
|
|
|
|
|
static Terminal terminals[MAX_NTERMS];
|
static Terminal terminals[MAX_NTERMS];
|
static int numTerminals;
|
static int numTerminals;
|
|
|
|
|
/**************************************************************/
|
/**************************************************************/
|
|
|
|
|
static void rcvrCallback(int dev) {
|
static void rcvrCallback(int dev) {
|
int c;
|
int c;
|
|
|
if (debug) {
|
if (debug) {
|
cPrintf("\n**** TERM RCVR CALLBACK ****\n");
|
cPrintf("\n**** TERM RCVR CALLBACK ****\n");
|
}
|
}
|
timerStart(TERM_RCVR_USEC, rcvrCallback, dev);
|
timerStart(TERM_RCVR_USEC, rcvrCallback, dev);
|
c = fgetc(terminals[dev].in);
|
c = fgetc(terminals[dev].in);
|
if (c == EOF) {
|
if (c == EOF) {
|
/* no character typed */
|
/* no character typed */
|
return;
|
return;
|
}
|
}
|
/* any character typed */
|
/* any character typed */
|
terminals[dev].rcvrData = c & 0xFF;
|
terminals[dev].rcvrData = c & 0xFF;
|
terminals[dev].rcvrCtrl |= TERM_RCVR_RDY;
|
terminals[dev].rcvrCtrl |= TERM_RCVR_RDY;
|
if (terminals[dev].rcvrCtrl & TERM_RCVR_IEN) {
|
if (terminals[dev].rcvrCtrl & TERM_RCVR_IEN) {
|
/* raise terminal rcvr interrupt */
|
/* raise terminal rcvr interrupt */
|
cpuSetInterrupt(terminals[dev].rcvrIRQ);
|
cpuSetInterrupt(terminals[dev].rcvrIRQ);
|
}
|
}
|
}
|
}
|
|
|
|
|
static void xmtrCallback(int dev) {
|
static void xmtrCallback(int dev) {
|
if (debug) {
|
if (debug) {
|
cPrintf("\n**** TERM XMTR CALLBACK ****\n");
|
cPrintf("\n**** TERM XMTR CALLBACK ****\n");
|
}
|
}
|
fputc(terminals[dev].xmtrData & 0xFF, terminals[dev].out);
|
fputc(terminals[dev].xmtrData & 0xFF, terminals[dev].out);
|
terminals[dev].xmtrCtrl |= TERM_XMTR_RDY;
|
terminals[dev].xmtrCtrl |= TERM_XMTR_RDY;
|
if (terminals[dev].xmtrCtrl & TERM_XMTR_IEN) {
|
if (terminals[dev].xmtrCtrl & TERM_XMTR_IEN) {
|
/* raise terminal xmtr interrupt */
|
/* raise terminal xmtr interrupt */
|
cpuSetInterrupt(terminals[dev].xmtrIRQ);
|
cpuSetInterrupt(terminals[dev].xmtrIRQ);
|
}
|
}
|
}
|
}
|
|
|
|
|
/**************************************************************/
|
/**************************************************************/
|
|
|
|
|
Word termRead(Word addr) {
|
Word termRead(Word addr) {
|
int dev, reg;
|
int dev, reg;
|
Word data;
|
Word data;
|
|
|
if (debug) {
|
if (debug) {
|
cPrintf("\n**** TERM READ from 0x%08X", addr);
|
cPrintf("\n**** TERM READ from 0x%08X", addr);
|
}
|
}
|
dev = addr >> 12;
|
dev = addr >> 12;
|
if (dev >= numTerminals) {
|
if (dev >= numTerminals) {
|
/* illegal device */
|
/* illegal device */
|
throwException(EXC_BUS_TIMEOUT);
|
throwException(EXC_BUS_TIMEOUT);
|
}
|
}
|
reg = addr & 0x0FFF;
|
reg = addr & 0x0FFF;
|
if (reg == TERM_RCVR_CTRL) {
|
if (reg == TERM_RCVR_CTRL) {
|
data = terminals[dev].rcvrCtrl;
|
data = terminals[dev].rcvrCtrl;
|
} else
|
} else
|
if (reg == TERM_RCVR_DATA) {
|
if (reg == TERM_RCVR_DATA) {
|
terminals[dev].rcvrCtrl &= ~TERM_RCVR_RDY;
|
terminals[dev].rcvrCtrl &= ~TERM_RCVR_RDY;
|
if (terminals[dev].rcvrCtrl & TERM_RCVR_IEN) {
|
if (terminals[dev].rcvrCtrl & TERM_RCVR_IEN) {
|
/* lower terminal rcvr interrupt */
|
/* lower terminal rcvr interrupt */
|
cpuResetInterrupt(terminals[dev].rcvrIRQ);
|
cpuResetInterrupt(terminals[dev].rcvrIRQ);
|
}
|
}
|
data = terminals[dev].rcvrData;
|
data = terminals[dev].rcvrData;
|
} else
|
} else
|
if (reg == TERM_XMTR_CTRL) {
|
if (reg == TERM_XMTR_CTRL) {
|
data = terminals[dev].xmtrCtrl;
|
data = terminals[dev].xmtrCtrl;
|
} else
|
} else
|
if (reg == TERM_XMTR_DATA) {
|
if (reg == TERM_XMTR_DATA) {
|
/* this register is write-only */
|
/* this register is write-only */
|
throwException(EXC_BUS_TIMEOUT);
|
throwException(EXC_BUS_TIMEOUT);
|
} else {
|
} else {
|
/* illegal register */
|
/* illegal register */
|
throwException(EXC_BUS_TIMEOUT);
|
throwException(EXC_BUS_TIMEOUT);
|
}
|
}
|
if (debug) {
|
if (debug) {
|
cPrintf(", data = 0x%08X ****\n", data);
|
cPrintf(", data = 0x%08X ****\n", data);
|
}
|
}
|
return data;
|
return data;
|
}
|
}
|
|
|
|
|
void termWrite(Word addr, Word data) {
|
void termWrite(Word addr, Word data) {
|
int dev, reg;
|
int dev, reg;
|
|
|
if (debug) {
|
if (debug) {
|
cPrintf("\n**** TERM WRITE to 0x%08X, data = 0x%08X ****\n",
|
cPrintf("\n**** TERM WRITE to 0x%08X, data = 0x%08X ****\n",
|
addr, data);
|
addr, data);
|
}
|
}
|
dev = addr >> 12;
|
dev = addr >> 12;
|
if (dev >= numTerminals) {
|
if (dev >= numTerminals) {
|
/* illegal device */
|
/* illegal device */
|
throwException(EXC_BUS_TIMEOUT);
|
throwException(EXC_BUS_TIMEOUT);
|
}
|
}
|
reg = addr & 0x0FFF;
|
reg = addr & 0x0FFF;
|
if (reg == TERM_RCVR_CTRL) {
|
if (reg == TERM_RCVR_CTRL) {
|
if (data & TERM_RCVR_IEN) {
|
if (data & TERM_RCVR_IEN) {
|
terminals[dev].rcvrCtrl |= TERM_RCVR_IEN;
|
terminals[dev].rcvrCtrl |= TERM_RCVR_IEN;
|
} else {
|
} else {
|
terminals[dev].rcvrCtrl &= ~TERM_RCVR_IEN;
|
terminals[dev].rcvrCtrl &= ~TERM_RCVR_IEN;
|
}
|
}
|
if (data & TERM_RCVR_RDY) {
|
if (data & TERM_RCVR_RDY) {
|
terminals[dev].rcvrCtrl |= TERM_RCVR_RDY;
|
terminals[dev].rcvrCtrl |= TERM_RCVR_RDY;
|
} else {
|
} else {
|
terminals[dev].rcvrCtrl &= ~TERM_RCVR_RDY;
|
terminals[dev].rcvrCtrl &= ~TERM_RCVR_RDY;
|
}
|
}
|
if ((terminals[dev].rcvrCtrl & TERM_RCVR_IEN) != 0 &&
|
if ((terminals[dev].rcvrCtrl & TERM_RCVR_IEN) != 0 &&
|
(terminals[dev].rcvrCtrl & TERM_RCVR_RDY) != 0) {
|
(terminals[dev].rcvrCtrl & TERM_RCVR_RDY) != 0) {
|
/* raise terminal rcvr interrupt */
|
/* raise terminal rcvr interrupt */
|
cpuSetInterrupt(terminals[dev].rcvrIRQ);
|
cpuSetInterrupt(terminals[dev].rcvrIRQ);
|
} else {
|
} else {
|
/* lower terminal rcvr interrupt */
|
/* lower terminal rcvr interrupt */
|
cpuResetInterrupt(terminals[dev].rcvrIRQ);
|
cpuResetInterrupt(terminals[dev].rcvrIRQ);
|
}
|
}
|
} else
|
} else
|
if (reg == TERM_RCVR_DATA) {
|
if (reg == TERM_RCVR_DATA) {
|
/* this register is read-only */
|
/* this register is read-only */
|
throwException(EXC_BUS_TIMEOUT);
|
throwException(EXC_BUS_TIMEOUT);
|
} else
|
} else
|
if (reg == TERM_XMTR_CTRL) {
|
if (reg == TERM_XMTR_CTRL) {
|
if (data & TERM_XMTR_IEN) {
|
if (data & TERM_XMTR_IEN) {
|
terminals[dev].xmtrCtrl |= TERM_XMTR_IEN;
|
terminals[dev].xmtrCtrl |= TERM_XMTR_IEN;
|
} else {
|
} else {
|
terminals[dev].xmtrCtrl &= ~TERM_XMTR_IEN;
|
terminals[dev].xmtrCtrl &= ~TERM_XMTR_IEN;
|
}
|
}
|
if (data & TERM_XMTR_RDY) {
|
if (data & TERM_XMTR_RDY) {
|
terminals[dev].xmtrCtrl |= TERM_XMTR_RDY;
|
terminals[dev].xmtrCtrl |= TERM_XMTR_RDY;
|
} else {
|
} else {
|
terminals[dev].xmtrCtrl &= ~TERM_XMTR_RDY;
|
terminals[dev].xmtrCtrl &= ~TERM_XMTR_RDY;
|
}
|
}
|
if ((terminals[dev].xmtrCtrl & TERM_XMTR_IEN) != 0 &&
|
if ((terminals[dev].xmtrCtrl & TERM_XMTR_IEN) != 0 &&
|
(terminals[dev].xmtrCtrl & TERM_XMTR_RDY) != 0) {
|
(terminals[dev].xmtrCtrl & TERM_XMTR_RDY) != 0) {
|
/* raise terminal xmtr interrupt */
|
/* raise terminal xmtr interrupt */
|
cpuSetInterrupt(terminals[dev].xmtrIRQ);
|
cpuSetInterrupt(terminals[dev].xmtrIRQ);
|
} else {
|
} else {
|
/* lower terminal xmtr interrupt */
|
/* lower terminal xmtr interrupt */
|
cpuResetInterrupt(terminals[dev].xmtrIRQ);
|
cpuResetInterrupt(terminals[dev].xmtrIRQ);
|
}
|
}
|
} else
|
} else
|
if (reg == TERM_XMTR_DATA) {
|
if (reg == TERM_XMTR_DATA) {
|
terminals[dev].xmtrData = data & 0xFF;
|
terminals[dev].xmtrData = data & 0xFF;
|
terminals[dev].xmtrCtrl &= ~TERM_XMTR_RDY;
|
terminals[dev].xmtrCtrl &= ~TERM_XMTR_RDY;
|
if (terminals[dev].xmtrCtrl & TERM_XMTR_IEN) {
|
if (terminals[dev].xmtrCtrl & TERM_XMTR_IEN) {
|
/* lower terminal xmtr interrupt */
|
/* lower terminal xmtr interrupt */
|
cpuResetInterrupt(terminals[dev].xmtrIRQ);
|
cpuResetInterrupt(terminals[dev].xmtrIRQ);
|
}
|
}
|
timerStart(TERM_XMTR_USEC, xmtrCallback, dev);
|
timerStart(TERM_XMTR_USEC, xmtrCallback, dev);
|
} else {
|
} else {
|
/* illegal register */
|
/* illegal register */
|
throwException(EXC_BUS_TIMEOUT);
|
throwException(EXC_BUS_TIMEOUT);
|
}
|
}
|
}
|
}
|
|
|
|
|
/**************************************************************/
|
/**************************************************************/
|
|
|
|
|
void termReset(void) {
|
void termReset(void) {
|
int i;
|
int i;
|
|
|
cPrintf("Resetting Terminals...\n");
|
cPrintf("Resetting Terminals...\n");
|
for (i = 0; i < numTerminals; i++) {
|
for (i = 0; i < numTerminals; i++) {
|
terminals[i].rcvrCtrl = 0;
|
terminals[i].rcvrCtrl = 0;
|
terminals[i].rcvrData = 0;
|
terminals[i].rcvrData = 0;
|
terminals[i].rcvrIRQ = IRQ_TERM_0_RCVR + 2 * i;
|
terminals[i].rcvrIRQ = IRQ_TERM_0_RCVR + 2 * i;
|
timerStart(TERM_RCVR_USEC, rcvrCallback, i);
|
timerStart(TERM_RCVR_USEC, rcvrCallback, i);
|
terminals[i].xmtrCtrl = TERM_XMTR_RDY;
|
terminals[i].xmtrCtrl = TERM_XMTR_RDY;
|
terminals[i].xmtrData = 0;
|
terminals[i].xmtrData = 0;
|
terminals[i].xmtrIRQ = IRQ_TERM_0_XMTR + 2 * i;
|
terminals[i].xmtrIRQ = IRQ_TERM_0_XMTR + 2 * i;
|
}
|
}
|
}
|
}
|
|
|
|
|
static int openPty(int *master, int *slave, char *name) {
|
static int openPty(int *master, int *slave, char *name) {
|
/* try to open master */
|
/* try to open master */
|
strcpy(name, "/dev/ptmx");
|
strcpy(name, "/dev/ptmx");
|
*master = open(name, O_RDWR | O_NONBLOCK);
|
*master = open(name, O_RDWR | O_NONBLOCK);
|
if (*master < 0) {
|
if (*master < 0) {
|
/* open failed */
|
/* open failed */
|
return -1;
|
return -1;
|
}
|
}
|
grantpt(*master);
|
grantpt(*master);
|
unlockpt(*master);
|
unlockpt(*master);
|
/* master opened, try to open slave */
|
/* master opened, try to open slave */
|
strcpy(name, ptsname(*master));
|
strcpy(name, ptsname(*master));
|
*slave = open(name, O_RDWR | O_NONBLOCK);
|
*slave = open(name, O_RDWR | O_NONBLOCK);
|
if (*slave < 0) {
|
if (*slave < 0) {
|
/* open failed, close master */
|
/* open failed, close master */
|
close(*master);
|
close(*master);
|
return -1;
|
return -1;
|
}
|
}
|
/* all is well */
|
/* all is well */
|
return 0;
|
return 0;
|
}
|
}
|
|
|
|
|
static void makeRaw(struct termios *tp) {
|
static void makeRaw(struct termios *tp) {
|
tp->c_iflag &= ~(IGNBRK|BRKINT|PARMRK|ISTRIP|INLCR|IGNCR|ICRNL|IXON);
|
tp->c_iflag &= ~(IGNBRK|BRKINT|PARMRK|ISTRIP|INLCR|IGNCR|ICRNL|IXON);
|
tp->c_oflag &= ~OPOST;
|
tp->c_oflag &= ~OPOST;
|
tp->c_lflag &= ~(ECHO|ECHONL|ICANON|ISIG|IEXTEN);
|
tp->c_lflag &= ~(ECHO|ECHONL|ICANON|ISIG|IEXTEN);
|
tp->c_cflag &= ~(CSIZE|PARENB);
|
tp->c_cflag &= ~(CSIZE|PARENB);
|
tp->c_cflag |= CS8;
|
tp->c_cflag |= CS8;
|
}
|
}
|
|
|
|
|
void termInit(int numTerms) {
|
void termInit(int numTerms) {
|
int master, slave;
|
int master, slave;
|
char ptyName[100];
|
char ptyName[100];
|
char ptyTitle[100];
|
char ptyTitle[100];
|
struct termios termios;
|
struct termios termios;
|
int i;
|
int i;
|
|
|
numTerminals = numTerms;
|
numTerminals = numTerms;
|
for (i = 0; i < numTerminals; i++) {
|
for (i = 0; i < numTerminals; i++) {
|
/* open pseudo terminal */
|
/* open pseudo terminal */
|
if (openPty(&master, &slave, ptyName) < 0) {
|
if (openPty(&master, &slave, ptyName) < 0) {
|
error("cannot open pseudo terminal %d", i);
|
error("cannot open pseudo terminal %d", i);
|
}
|
}
|
if (debug) {
|
if (debug) {
|
cPrintf("pseudo terminal '%s': master fd = %d, slave fd = %d\n",
|
cPrintf("pseudo terminal '%s': master fd = %d, slave fd = %d\n",
|
ptyName, master, slave);
|
ptyName, master, slave);
|
}
|
}
|
/* set mode to raw */
|
/* set mode to raw */
|
tcgetattr(slave, &termios);
|
tcgetattr(slave, &termios);
|
makeRaw(&termios);
|
makeRaw(&termios);
|
tcsetattr(slave, TCSANOW, &termios);
|
tcsetattr(slave, TCSANOW, &termios);
|
/* fork and exec a new xterm */
|
/* fork and exec a new xterm */
|
terminals[i].pid = fork();
|
terminals[i].pid = fork();
|
if (terminals[i].pid < 0) {
|
if (terminals[i].pid < 0) {
|
error("cannot fork xterm process %d", i);
|
error("cannot fork xterm process %d", i);
|
}
|
}
|
if (terminals[i].pid == 0) {
|
if (terminals[i].pid == 0) {
|
/* terminal process */
|
/* terminal process */
|
setpgid(0, 0);
|
setpgid(0, 0);
|
close(2);
|
close(2);
|
close(master);
|
close(master);
|
sprintf(ptyName, "-Sab%d", slave);
|
sprintf(ptyName, "-Sab%d", slave);
|
sprintf(ptyTitle, "ECO32 Terminal %d", i);
|
sprintf(ptyTitle, "ECO32 Terminal %d", i);
|
execlp("xterm", "xterm", "-title", ptyTitle, ptyName, NULL);
|
execlp("xterm", "xterm", "-title", ptyTitle, ptyName, NULL);
|
error("cannot exec xterm process %d", i);
|
error("cannot exec xterm process %d", i);
|
}
|
}
|
fcntl(master, F_SETFL, O_NONBLOCK);
|
fcntl(master, F_SETFL, O_NONBLOCK);
|
terminals[i].in = fdopen(master, "r");
|
terminals[i].in = fdopen(master, "r");
|
setvbuf(terminals[i].in, NULL, _IONBF, 0);
|
setvbuf(terminals[i].in, NULL, _IONBF, 0);
|
terminals[i].out = fdopen(master, "w");
|
terminals[i].out = fdopen(master, "w");
|
setvbuf(terminals[i].out, NULL, _IONBF, 0);
|
setvbuf(terminals[i].out, NULL, _IONBF, 0);
|
/* skip the window id written by xterm */
|
/* skip the window id written by xterm */
|
while (fgetc(terminals[i].in) != '\n') ;
|
while (fgetc(terminals[i].in) != '\n') ;
|
}
|
}
|
termReset();
|
termReset();
|
}
|
}
|
|
|
|
|
void termExit(void) {
|
void termExit(void) {
|
int i;
|
int i;
|
|
|
/* kill and wait for all xterm processes */
|
/* kill and wait for all xterm processes */
|
for (i = 0; i < numTerminals; i++) {
|
for (i = 0; i < numTerminals; i++) {
|
if (terminals[i].pid > 0) {
|
if (terminals[i].pid > 0) {
|
kill(terminals[i].pid, SIGKILL);
|
kill(terminals[i].pid, SIGKILL);
|
waitpid(terminals[i].pid, NULL, 0);
|
waitpid(terminals[i].pid, NULL, 0);
|
}
|
}
|
}
|
}
|
}
|
}
|
|
|