/* This file is part of GDB. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /* This started out life as code shared between the nindy monitor and GDB. For various reasons, this is no longer true. Eventually, it probably should be merged into remote-nindy.c. */ /****************************************************************************** * * NINDY INTERFACE ROUTINES * * The caller of these routines should be aware that: * * (1) ninConnect() should be called to open communications with the * remote NINDY board before any of the other routines are invoked. * * (2) almost all interactions are driven by the host: nindy sends information * in response to host commands. * * (3) the lone exception to (2) is the single character DLE (^P, 0x10). * Receipt of a DLE from NINDY indicates that the application program * running under NINDY has stopped execution and that NINDY is now * available to talk to the host (all other communication received after * the application has been started should be presumed to come from the * application and should be passed on by the host to stdout). * * (4) the reason the application program stopped can be determined with the * ninStopWhy() function. There are three classes of stop reasons: * * (a) the application has terminated execution. * The host should take appropriate action. * * (b) the application had a fault or trace event. * The host should take appropriate action. * * (c) the application wishes to make a service request (srq) of the host; * e.g., to open/close a file, read/write a file, etc. The ninSrq() * function should be called to determine the nature of the request * and process it. */ #include <stdio.h> #include "defs.h" #include "serial.h" #ifdef ANSI_PROTOTYPES #include <stdarg.h> #else #include <varargs.h> #endif #if !defined (HAVE_TERMIOS) && !defined (HAVE_TERMIO) && !defined (HAVE_SGTTY) #define HAVE_SGTTY #endif #ifdef HAVE_SGTTY #include <sys/ioctl.h> #endif #include <sys/types.h> /* Needed by file.h on Sys V */ #include <sys/file.h> #include <signal.h> #include <sys/stat.h> #if 0 #include "ttycntl.h" #endif #include "block_io.h" #include "gdb_wait.h" #include "env.h" #define DLE 0x10 /* ^P */ #define XON 0x11 /* ^Q */ #define XOFF 0x13 /* ^S */ #define ESC 0x1b #define TIMEOUT -1 int quiet = 0; /* 1 => stifle unnecessary messages */ struct serial *nindy_serial; static int old_nindy = 0; /* 1 => use old (hex) communication protocol */ static ninStrGet(); /**************************** * * * MISCELLANEOUS UTILTIES * * * ****************************/ /****************************************************************************** * say: * This is a printf that takes at most two arguments (in addition to the * format string) and that outputs nothing if verbose output has been * suppressed. *****************************************************************************/ /* VARARGS */ static void #ifdef ANSI_PROTOTYPES say (char *fmt, ...) #else say (va_alist) va_dcl #endif { va_list args; #ifdef ANSI_PROTOTYPES va_start(args, fmt); #else char *fmt; va_start (args); fmt = va_arg (args, char *); #endif if (!quiet) { vfprintf_unfiltered (gdb_stdout, fmt, args); gdb_flush (gdb_stdout); } va_end (args); } /****************************************************************************** * exists: * Creates a full pathname by concatenating up to three name components * onto a specified base name; optionally looks up the base name as a * runtime environment variable; and checks to see if the file or * directory specified by the pathname actually exists. * * Returns: the full pathname if it exists, NULL otherwise. * (returned pathname is in malloc'd memory and must be freed * by caller). *****************************************************************************/ static char * exists( base, c1, c2, c3, env ) char *base; /* Base directory of path */ char *c1, *c2, *c3; /* Components (subdirectories and/or file name) to be * appended onto the base directory name. One or * more may be omitted by passing NULL pointers. */ int env; /* If 1, '*base' is the name of an environment variable * to be examined for the base directory name; * otherwise, '*base' is the actual name of the * base directory. */ { struct stat buf;/* For call to 'stat' -- never examined */ char *path; /* Pointer to full pathname (malloc'd memory) */ int len; /* Length of full pathname (incl. terminator) */ extern char *getenv(); if ( env ){ base = getenv( base ); if ( base == NULL ){ return NULL; } } len = strlen(base) + 4; /* +4 for terminator and "/" before each component */ if ( c1 != NULL ){ len += strlen(c1); } if ( c2 != NULL ){ len += strlen(c2); } if ( c3 != NULL ){ len += strlen(c3); } path = xmalloc (len); strcpy( path, base ); if ( c1 != NULL ){ strcat( path, "/" ); strcat( path, c1 ); if ( c2 != NULL ){ strcat( path, "/" ); strcat( path, c2 ); if ( c3 != NULL ){ strcat( path, "/" ); strcat( path, c3 ); } } } if ( stat(path,&buf) != 0 ){ free( path ); path = NULL; } return path; } /***************************** * * * LOW-LEVEL COMMUNICATION * * * *****************************/ /* Read *exactly* N characters from the NINDY tty, and put them in *BUF. Translate escape sequences into single characters, counting each such sequence as 1 character. An escape sequence consists of ESC and a following character. The ESC is discarded and the other character gets bit 0x40 cleared -- thus ESC P == ^P, ESC S == ^S, ESC [ == ESC, etc. Return 1 if successful, 0 if more than TIMEOUT seconds pass without any input. */ static int rdnin (buf,n,timeout) unsigned char * buf; /* Where to place characters read */ int n; /* Number of characters to read */ int timeout; /* Timeout, in seconds */ { int escape_seen; /* 1 => last character of a read was an ESC */ int c; escape_seen = 0; while (n) { c = serial_readchar (nindy_serial, timeout); switch (c) { case SERIAL_ERROR: case SERIAL_TIMEOUT: case SERIAL_EOF: return 0; case ESC: escape_seen = 1; break; default: if (escape_seen) { escape_seen = 0; c &= ~0x40; } *buf++ = c; --n; break; } } return 1; } /****************************************************************************** * getpkt: * Read a packet from a remote NINDY, with error checking, into the * indicated buffer. * * Return packet status byte on success, TIMEOUT on failure. ******************************************************************************/ static int getpkt(buf) unsigned char *buf; { int i; unsigned char hdr[3]; /* Packet header: * hdr[0] = low byte of message length * hdr[1] = high byte of message length * hdr[2] = message status */ int cnt; /* Message length (status byte + data) */ unsigned char cs_calc; /* Checksum calculated */ unsigned char cs_recv; /* Checksum received */ static char errfmt[] = "Bad checksum (recv=0x%02x; calc=0x%02x); retrying\r\n"; while (1){ if ( !rdnin(hdr,3,5) ){ return TIMEOUT; } cnt = (hdr[1]<<8) + hdr[0] - 1; /* -1 for status byte (already read) */ /* Caller's buffer may only be big enough for message body, * without status byte and checksum, so make sure to read * checksum into a separate buffer. */ if ( !rdnin(buf,cnt,5) || !rdnin(&cs_recv,1,5) ){ return TIMEOUT; } /* Calculate checksum */ cs_calc = hdr[0] + hdr[1] + hdr[2]; for ( i = 0; i < cnt; i++ ){ cs_calc += buf[i]; } if ( cs_calc == cs_recv ){ serial_write (nindy_serial, "+", 1); return hdr[2]; } /* Bad checksum: report, send NAK, and re-receive */ fprintf(stderr, errfmt, cs_recv, cs_calc ); serial_write (nindy_serial, "-", 1); } } /****************************************************************************** * putpkt: * Send a packet to NINDY, checksumming it and converting special * characters to escape sequences. ******************************************************************************/ /* This macro puts the character 'c' into the buffer pointed at by 'p', * and increments the pointer. If 'c' is one of the 4 special characters * in the transmission protocol, it is converted into a 2-character * escape sequence. */ #define PUTBUF(c,p) \ if ( c == DLE || c == ESC || c == XON || c == XOFF ){ \ *p++ = ESC; \ *p++ = c | 0x40; \ } else { \ *p++ = c; \ } static putpkt( msg, len ) unsigned char *msg; /* Command to be sent, without lead ^P (\020) or checksum */ int len; /* Number of bytes in message */ { static char *buf = NULL;/* Local buffer -- build packet here */ static int maxbuf = 0; /* Current length of buffer */ unsigned char ack; /* Response received from NINDY */ unsigned char checksum; /* Packet checksum */ char *p; /* Pointer into buffer */ int lenhi, lenlo; /* High and low bytes of message length */ int i; /* Make sure local buffer is big enough. Must include space for * packet length, message body, and checksum. And in the worst * case, each character would expand into a 2-character escape * sequence. */ if ( maxbuf < ((2*len)+10) ){ if ( buf ){ free( buf ); } buf = xmalloc( maxbuf=((2*len)+10) ); } /* Attention, NINDY! */ serial_write (nindy_serial, "\020", 1); lenlo = len & 0xff; lenhi = (len>>8) & 0xff; checksum = lenlo + lenhi; p = buf; PUTBUF( lenlo, p ); PUTBUF( lenhi, p ); for ( i=0; i<len; i++ ){ PUTBUF( msg[i], p ); checksum += msg[i]; } PUTBUF( checksum, p ); /* Send checksummed message over and over until we get a positive ack */ serial_write (nindy_serial, buf, p - buf); while (1){ if ( !rdnin(&ack,1,5) ){ /* timed out */ fprintf(stderr,"ACK timed out; resending\r\n"); /* Attention, NINDY! */ serial_write (nindy_serial, "\020", 1); serial_write (nindy_serial, buf, p - buf); } else if ( ack == '+' ){ return; } else if ( ack == '-' ){ fprintf( stderr, "Remote NAK; resending\r\n" ); serial_write (nindy_serial, buf, p - buf); } else { fprintf( stderr, "Bad ACK, ignored: <%c>\r\n", ack ); } } } /****************************************************************************** * send: * Send a message to a remote NINDY. Check message status byte * for error responses. If no error, return NINDY reponse (if any). ******************************************************************************/ static send( out, len, in ) unsigned char *out; /* Message to be sent to NINDY */ int len; /* Number of meaningful bytes in out buffer */ unsigned char *in; /* Where to put response received from NINDY */ { char *fmt; int status; static char *errmsg[] = { "", /* 0 */ "Buffer overflow", /* 1 */ "Unknown command", /* 2 */ "Wrong amount of data to load register(s)", /* 3 */ "Missing command argument(s)", /* 4 */ "Odd number of digits sent to load memory", /* 5 */ "Unknown register name", /* 6 */ "No such memory segment", /* 7 */ "No breakpoint available", /* 8 */ "Can't set requested baud rate", /* 9 */ }; # define NUMERRS ( sizeof(errmsg) / sizeof(errmsg[0]) ) static char err1[] = "Unknown error response from NINDY: #%d\r\n"; static char err2[] = "Error response #%d from NINDY: %s\r\n"; while (1){ putpkt(out,len); status = getpkt(in); if ( status == TIMEOUT ){ fprintf( stderr, "Response timed out; resending\r\n" ); } else { break; } } if ( status ){ fmt = status > NUMERRS ? err1 : err2; fprintf( stderr, fmt, status, errmsg[status] ); abort(); } } /************************ * * * BAUD RATE ROUTINES * * * ************************/ /* Table of baudrates known to be acceptable to NINDY. Each baud rate * appears both as character string and as a Unix baud rate constant. */ struct baudrate { char *string; int rate; }; static struct baudrate baudtab[] = { "1200", 1200, "2400", 2400, "4800", 4800, "9600", 9600, "19200", 19200, "38400", 38400, NULL, 0 /* End of table */ }; /****************************************************************************** * parse_baudrate: * Look up the passed baud rate in the baudrate table. If found, change * our internal record of the current baud rate, but don't do anything * about the tty just now. * * Return pointer to baudrate structure on success, NULL on failure. ******************************************************************************/ static struct baudrate * parse_baudrate(s) char *s; /* Desired baud rate, as an ASCII (decimal) string */ { int i; for ( i=0; baudtab[i].string != NULL; i++ ){ if ( !strcmp(baudtab[i].string,s) ){ return &baudtab[i]; } } return NULL; } /****************************************************************************** * try_baudrate: * Try speaking to NINDY via the specified file descriptor at the * specified baudrate. Assume success if we can send an empty command * with a bogus checksum and receive a NAK (response of '-') back within * one second. * * Return 1 on success, 0 on failure. ***************************************************************************/ static int try_baudrate (serial, brp) struct serial *serial; struct baudrate *brp; { unsigned char c; /* Set specified baud rate and flush all pending input */ serial_setbaudrate (serial, brp->rate); tty_flush (serial); /* Send empty command with bad checksum, hope for NAK ('-') response */ serial_write (serial, "\020\0\0\001", 4); /* Anything but a quick '-', including error, eof, or timeout, means that this baudrate doesn't work. */ return serial_readchar (serial, 1) == '-'; } /****************************************************************************** * autobaud: * Get NINDY talking over the specified file descriptor at the specified * baud rate. First see if NINDY's already talking at 'baudrate'. If * not, run through all the legal baudrates in 'baudtab' until one works, * and then tell NINDY to talk at 'baudrate' instead. ******************************************************************************/ static autobaud( serial, brp ) struct serial *serial; struct baudrate *brp; { int i; int failures; say("NINDY at wrong baud rate? Trying to autobaud...\n"); failures = i = 0; while (1) { say( "\r%s... ", baudtab[i].string ); if (try_baudrate(serial, &baudtab[i])) { break; } if (baudtab[++i].string == NULL) { /* End of table -- wraparound */ i = 0; if ( failures++ ) { say("\nAutobaud failed again. Giving up.\n"); exit(1); } else { say("\nAutobaud failed. Trying again...\n"); } } } /* Found NINDY's current baud rate; now change it. */ say("Changing NINDY baudrate to %s\n", brp->string); ninBaud (brp->string); /* Change our baud rate back to rate to which we just set NINDY. */ serial_setbaudrate (serial, brp->rate); } /********************************** * * * NINDY INTERFACE ROUTINES * * * * ninConnect *MUST* be the first * * one of these routines called. * **********************************/ /****************************************************************************** * ninBaud: * Ask NINDY to change the baud rate on its serial port. * Assumes we know the baud rate at which NINDY's currently talking. ******************************************************************************/ ninBaud( baudrate ) char *baudrate; /* Desired baud rate, as a string of ASCII decimal * digits. */ { unsigned char msg[100]; tty_flush (nindy_serial); if (old_nindy) { char *p; /* Pointer into buffer */ unsigned char csum; /* Calculated checksum */ /* Can't use putpkt() because after the baudrate change NINDY's ack/nak will look like gibberish. */ for (p=baudrate, csum=020+'z'; *p; p++) { csum += *p; } sprintf (msg, "\020z%s#%02x", baudrate, csum); serial_write (nindy_serial, msg, strlen (msg)); } else { /* Can't use "send" because NINDY reply will be unreadable after baud rate change. */ sprintf( msg, "z%s", baudrate ); putpkt( msg, strlen(msg)+1 ); /* "+1" to send terminator too */ } } /****************************************************************************** * ninBptDel: * Ask NINDY to delete the specified type of *hardware* breakpoint at * the specified address. If the 'addr' is -1, all breakpoints of * the specified type are deleted. ***************************************************************************/ ninBptDel( addr, type ) long addr; /* Address in 960 memory */ char type; /* 'd' => data bkpt, 'i' => instruction breakpoint */ { unsigned char buf[10]; if ( old_nindy ){ OninBptDel( addr, type == 'd' ? 1 : 0 ); return; } buf[0] = 'b'; buf[1] = type; if ( addr == -1 ){ send( buf, 2, NULL ); } else { store_unsigned_integer (&buf[2], 4, addr); send( buf, 6, NULL ); } } /****************************************************************************** * ninBptSet: * Ask NINDY to set the specified type of *hardware* breakpoint at * the specified address. ******************************************************************************/ ninBptSet( addr, type ) long addr; /* Address in 960 memory */ char type; /* 'd' => data bkpt, 'i' => instruction breakpoint */ { unsigned char buf[10]; if ( old_nindy ){ OninBptSet( addr, type == 'd' ? 1 : 0 ); return; } buf[0] = 'B'; buf[1] = type; store_unsigned_integer (&buf[2], 4, addr); send( buf, 6, NULL ); } /****************************************************************************** * ninConnect: * Open the specified tty. Get communications working at the specified * baud rate. Flush any pending I/O on the tty. * * Return the file descriptor, or -1 on failure. ******************************************************************************/ int ninConnect( name, baudrate, brk, silent, old_protocol ) char *name; /* "/dev/ttyXX" to be opened */ char *baudrate;/* baud rate: a string of ascii decimal digits (eg,"9600")*/ int brk; /* 1 => send break to tty first thing after opening it*/ int silent; /* 1 => stifle unnecessary messages when talking to * this tty. */ int old_protocol; { int i; char *p; struct baudrate *brp; /* We will try each of the following paths when trying to open the tty */ static char *prefix[] = { "", "/dev/", "/dev/tty", NULL }; if ( old_protocol ){ old_nindy = 1; } quiet = silent; /* Make global to this file */ for ( i=0; prefix[i] != NULL; i++ ){ p = xmalloc(strlen(prefix[i]) + strlen(name) + 1 ); strcpy( p, prefix[i] ); strcat( p, name ); nindy_serial = serial_open (p); if (nindy_serial != NULL) { #ifdef TIOCEXCL /* Exclusive use mode (hp9000 does not support it) */ ioctl(nindy_serial->fd,TIOCEXCL,NULL); #endif serial_raw (nindy_serial); if (brk) { serial_send_break (nindy_serial); } brp = parse_baudrate( baudrate ); if ( brp == NULL ){ say("Illegal baudrate %s ignored; using 9600\n", baudrate); brp = parse_baudrate( "9600" ); } if ( !try_baudrate(nindy_serial, brp) ){ autobaud(nindy_serial, brp); } tty_flush (nindy_serial); say( "Connected to %s\n", p ); free(p); break; } free(p); } return 0; } #if 0 /* Currently unused; shouldn't we be doing this on target_kill and perhaps target_mourn? FIXME. */ /****************************************************************************** * ninGdbExit: * Ask NINDY to leave GDB mode and print a NINDY prompt. ****************************************************************************/ ninGdbExit() { if ( old_nindy ){ OninGdbExit(); return; } putpkt((unsigned char *) "E", 1 ); } #endif /****************************************************************************** * ninGo: * Ask NINDY to start or continue execution of an application program * in it's memory at the current ip. ******************************************************************************/ ninGo( step_flag ) int step_flag; /* 1 => run in single-step mode */ { if ( old_nindy ){ OninGo( step_flag ); return; } putpkt((unsigned char *) (step_flag ? "s" : "c"), 1 ); } /****************************************************************************** * ninMemGet: * Read a string of bytes from NINDY's address space (960 memory). ******************************************************************************/ int ninMemGet(ninaddr, hostaddr, len) long ninaddr; /* Source address, in the 960 memory space */ unsigned char *hostaddr; /* Destination address, in our memory space */ int len; /* Number of bytes to read */ { unsigned char buf[BUFSIZE+20]; int cnt; /* Number of bytes in next transfer */ int origlen = len; if ( old_nindy ){ OninMemGet(ninaddr, hostaddr, len); return; } for ( ; len > 0; len -= BUFSIZE ){ cnt = len > BUFSIZE ? BUFSIZE : len; buf[0] = 'm'; store_unsigned_integer (&buf[1], 4, ninaddr); buf[5] = cnt & 0xff; buf[6] = (cnt>>8) & 0xff; send( buf, 7, hostaddr ); ninaddr += cnt; hostaddr += cnt; } return origlen; } /****************************************************************************** * ninMemPut: * Write a string of bytes into NINDY's address space (960 memory). ******************************************************************************/ int ninMemPut( ninaddr, hostaddr, len ) long ninaddr; /* Destination address, in NINDY memory space */ unsigned char *hostaddr; /* Source address, in our memory space */ int len; /* Number of bytes to write */ { unsigned char buf[BUFSIZE+20]; int cnt; /* Number of bytes in next transfer */ int origlen = len; if ( old_nindy ){ OninMemPut( ninaddr, hostaddr, len ); return; } for ( ; len > 0; len -= BUFSIZE ){ cnt = len > BUFSIZE ? BUFSIZE : len; buf[0] = 'M'; store_unsigned_integer (&buf[1], 4, ninaddr); memcpy(buf + 5, hostaddr, cnt); send( buf, cnt+5, NULL ); ninaddr += cnt; hostaddr += cnt; } return origlen; } /****************************************************************************** * ninRegGet: * Retrieve the contents of a 960 register, and return them as a long * in host byte order. * * THIS ROUTINE CAN ONLY BE USED TO READ THE LOCAL, GLOBAL, AND * ip/ac/pc/tc REGISTERS. * ******************************************************************************/ long ninRegGet( regname ) char *regname; /* Register name recognized by NINDY, subject to the * above limitations. */ { unsigned char outbuf[10]; unsigned char inbuf[20]; if ( old_nindy ){ return OninRegGet( regname ); } sprintf( outbuf, "u%s:", regname ); send( outbuf, strlen(outbuf), inbuf ); return extract_unsigned_integer (inbuf, 4); } /****************************************************************************** * ninRegPut: * Set the contents of a 960 register. * * THIS ROUTINE CAN ONLY BE USED TO SET THE LOCAL, GLOBAL, AND * ip/ac/pc/tc REGISTERS. * ******************************************************************************/ ninRegPut( regname, val ) char *regname; /* Register name recognized by NINDY, subject to the * above limitations. */ long val; /* New contents of register, in host byte-order */ { unsigned char buf[20]; int len; if ( old_nindy ){ OninRegPut( regname, val ); return; } sprintf( buf, "U%s:", regname ); len = strlen(buf); store_unsigned_integer (&buf[len], 4, val); send( buf, len+4, NULL ); } /****************************************************************************** * ninRegsGet: * Get a dump of the contents of the entire 960 register set. The * individual registers appear in the dump in the following order: * * pfp sp rip r3 r4 r5 r6 r7 * r8 r9 r10 r11 r12 r13 r14 r15 * g0 g1 g2 g3 g4 g5 g6 g7 * g8 g9 g10 g11 g12 g13 g14 fp * pc ac ip tc fp0 fp1 fp2 fp3 * * Each individual register comprises exactly 4 bytes, except for * fp0-fp3, which are 8 bytes. All register values are in 960 * (little-endian) byte order. * ******************************************************************************/ ninRegsGet( regp ) unsigned char *regp; /* Where to place the register dump */ { if ( old_nindy ){ OninRegsGet( regp ); return; } send( (unsigned char *) "r", 1, regp ); } /****************************************************************************** * ninRegsPut: * Initialize the entire 960 register set to a specified set of values. * The format of the register value data should be the same as that * returned by ninRegsGet. * * WARNING: * All register values must be in 960 (little-endian) byte order. * ******************************************************************************/ ninRegsPut( regp ) char *regp; /* Pointer to desired values of registers */ { /* Number of bytes that we send to nindy. I believe this is defined by the protocol (it does not agree with REGISTER_BYTES). */ #define NINDY_REGISTER_BYTES ((36*4) + (4*8)) unsigned char buf[NINDY_REGISTER_BYTES+10]; if ( old_nindy ){ OninRegsPut( regp ); return; } buf[0] = 'R'; memcpy(buf+1, regp, NINDY_REGISTER_BYTES ); send( buf, NINDY_REGISTER_BYTES+1, NULL ); } /****************************************************************************** * ninReset: * Ask NINDY to perform a soft reset; wait for the reset to complete. * ******************************************************************************/ ninReset() { unsigned char ack; if ( old_nindy ){ OninReset(); return; } while (1){ putpkt((unsigned char *) "X", 1 ); while (1){ if ( !rdnin(&ack,1,5) ){ /* Timed out */ break; /* Resend */ } if ( ack == '+' ){ return; } } } } /****************************************************************************** * ninSrq: * Assume NINDY has stopped execution of the 960 application program in * order to process a host service request (srq). Ask NINDY for the * srq arguments, perform the requested service, and send an "srq * complete" message so NINDY will return control to the application. * ******************************************************************************/ ninSrq() { /* FIXME: Imposes arbitrary limits on lengths of pathnames and such. */ unsigned char buf[BUFSIZE]; int retcode; unsigned char srqnum; int i; int offset; int arg[MAX_SRQ_ARGS]; if ( old_nindy ){ OninSrq(); return; } /* Get srq number and arguments */ send((unsigned char *) "!", 1, buf ); srqnum = buf[0]; for ( i=0, offset=1; i < MAX_SRQ_ARGS; i++, offset+=4 ){ arg[i] = extract_unsigned_integer (&buf[offset], 4); } /* Process Srq */ switch( srqnum ){ case BS_CLOSE: /* args: file descriptor */ if ( arg[0] > 2 ){ retcode = close( arg[0] ); } else { retcode = 0; } break; case BS_CREAT: /* args: filename, mode */ ninStrGet( arg[0], buf ); retcode = creat(buf,arg[1]); break; case BS_OPEN: /* args: filename, flags, mode */ ninStrGet( arg[0], buf ); retcode = open(buf,arg[1],arg[2]); break; case BS_READ: /* args: file descriptor, buffer, count */ retcode = read(arg[0],buf,arg[2]); if ( retcode > 0 ){ ninMemPut( arg[1], buf, retcode ); } break; case BS_SEEK: /* args: file descriptor, offset, whence */ retcode = lseek(arg[0],arg[1],arg[2]); break; case BS_WRITE: /* args: file descriptor, buffer, count */ ninMemGet( arg[1], buf, arg[2] ); retcode = write(arg[0],buf,arg[2]); break; default: retcode = -1; break; } /* Send request termination status to NINDY */ buf[0] = 'e'; store_unsigned_integer (&buf[1], 4, retcode); send( buf, 5, NULL ); } /****************************************************************************** * ninStopWhy: * Assume the application program has stopped (i.e., a DLE was received * from NINDY). Ask NINDY for status information describing the * reason for the halt. * * Returns a non-zero value if the user program has exited, 0 otherwise. * Also returns the following information, through passed pointers: * - why: an exit code if program the exited; otherwise the reason * why the program halted (see stop.h for values). * - contents of register ip (little-endian byte order) * - contents of register sp (little-endian byte order) * - contents of register fp (little-endian byte order) ******************************************************************************/ char ninStopWhy( whyp, ipp, fpp, spp ) unsigned char *whyp; /* Return the 'why' code through this pointer */ long *ipp; /* Return contents of register ip through this pointer */ long *fpp; /* Return contents of register fp through this pointer */ long *spp; /* Return contents of register sp through this pointer */ { unsigned char buf[30]; extern char OninStopWhy (); if ( old_nindy ){ return OninStopWhy( whyp, ipp, fpp, spp ); } send((unsigned char *) "?", 1, buf ); *whyp = buf[1]; memcpy ((char *)ipp, &buf[2], sizeof (*ipp)); memcpy ((char *)fpp, &buf[6], sizeof (*ipp)); memcpy ((char *)spp, &buf[10], sizeof (*ipp)); return buf[0]; } /****************************************************************************** * ninStrGet: * Read a '\0'-terminated string of data out of the 960 memory space. * ******************************************************************************/ static ninStrGet( ninaddr, hostaddr ) unsigned long ninaddr; /* Address of string in NINDY memory space */ unsigned char *hostaddr; /* Address of the buffer to which string should * be copied. */ { unsigned char cmd[5]; cmd[0] = '"'; store_unsigned_integer (&cmd[1], 4, ninaddr); send( cmd, 5, hostaddr ); } #if 0 /* Not used. */ /****************************************************************************** * ninVersion: * Ask NINDY for version information about itself. * The information is sent as an ascii string in the form "x.xx,<arch>", * where, * x.xx is the version number * <arch> is the processor architecture: "KA", "KB", "MC", "CA" * * ******************************************************************************/ int ninVersion( p ) unsigned char *p; /* Where to place version string */ { if ( old_nindy ){ return OninVersion( p ); } send((unsigned char *) "v", 1, p ); return strlen(p); } #endif /* 0 */