* arc-tdep.c: new target dependent codes for arc processor.
authorKung Hsu <kung@cygnus>
Fri, 3 Feb 1995 00:23:22 +0000 (00:23 +0000)
committerKung Hsu <kung@cygnus>
Fri, 3 Feb 1995 00:23:22 +0000 (00:23 +0000)
        * remote-arc.c: new file for arc-specific protocol through
        parallel line.
        * ser-go32-para.c: new file for go32 parallel port communication.
        * .Sanitize: sanitize arc specific files out.

gdb/.Sanitize
gdb/arc-tdep.c [new file with mode: 0644]
gdb/remote-arc.c [new file with mode: 0644]
gdb/ser-go32-para.c [new file with mode: 0644]

index 1d4c1cbd321dae88812b7862b66b913fb832870a..ca0e98ca71390736a3bd17cff8a38bc16c190679 100644 (file)
@@ -342,6 +342,8 @@ energize.h
 remote-sa.sparc.c
 state.c
 state.h
+arc-tdep.c
+remote-arc.c
 
 Do-last:
 
diff --git a/gdb/arc-tdep.c b/gdb/arc-tdep.c
new file mode 100644 (file)
index 0000000..8bea234
--- /dev/null
@@ -0,0 +1,361 @@
+/* ARC target-dependent stuff.
+   Copyright (C) 1995 Free Software Foundation, Inc.
+
+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., 675 Mass Ave, Cambridge, MA 02139, USA.  */
+
+#include "defs.h"
+#include "frame.h"
+#include "inferior.h"
+#include "gdbcore.h"
+#include "target.h"
+#include "floatformat.h"
+#include "symtab.h"
+
+static void codestream_read PARAMS ((unsigned int *, int));
+static void codestream_seek PARAMS ((int));
+static unsigned int codestream_fill PARAMS ((int));
+
+#define CODESTREAM_BUFSIZ 16 
+static CORE_ADDR codestream_next_addr;
+static CORE_ADDR codestream_addr;
+static unsigned int codestream_buf[CODESTREAM_BUFSIZ];
+static int codestream_off;
+static int codestream_cnt;
+
+#define codestream_tell() (codestream_addr + codestream_off)
+#define codestream_peek() (codestream_cnt == 0 ? \
+                          codestream_fill(1): codestream_buf[codestream_off])
+#define codestream_get() (codestream_cnt-- == 0 ? \
+                        codestream_fill(0) : codestream_buf[codestream_off++])
+#define OPMASK 0xf8000000
+
+static unsigned int 
+codestream_fill (peek_flag)
+    int peek_flag;
+{
+  codestream_addr = codestream_next_addr;
+  codestream_next_addr += CODESTREAM_BUFSIZ;
+  codestream_off = 0;
+  codestream_cnt = CODESTREAM_BUFSIZ;
+  read_memory (codestream_addr, (char *) codestream_buf, CODESTREAM_BUFSIZ);
+  
+  if (peek_flag)
+    return (codestream_peek());
+  else
+    return (codestream_get());
+}
+
+static void
+codestream_seek (place)
+    int place;
+{
+  codestream_next_addr = place / CODESTREAM_BUFSIZ;
+  codestream_next_addr *= CODESTREAM_BUFSIZ;
+  codestream_cnt = 0;
+  codestream_fill (1);
+  while (codestream_tell() != place)
+    codestream_get ();
+}
+
+static void
+codestream_read (buf, count)
+     unsigned int *buf;
+     int count;
+{
+  unsigned int *p;
+  int i;
+  p = buf;
+  for (i = 0; i < count; i++)
+    *p++ = codestream_get ();
+}
+
+/*
+ * find & return amound a local space allocated, and advance codestream to
+ * first register push (if any)
+ * if entry sequence doesn't make sense, return -1, and leave 
+ * codestream pointer random
+ */
+
+static long
+arc_get_frame_setup (pc)
+     int pc;
+{
+  unsigned int insn, n;
+
+  codestream_seek (pc);
+  insn = codestream_get ();
+
+  if (insn & OPMASK == 0x10000000)             /* st fp,[sp] */
+    {  
+      insn = codestream_get ();
+      if (insn & OPMASK != 0x10000000)         /* st blink,[sp,4] */
+       {
+         if (insn & OPMASK != 0x60000000)      /* for leaf, no st blink */
+           return -1;
+       }
+      else if (codestream_get () & OPMASK != 0x60000000)    /* mov fp,sp */
+       return (-1);
+
+      /* check for stack adjustment sub sp,nnn,sp */
+      insn = codestream_peek ();
+      if (insn & OPMASK == 0x50000000)
+       {
+         n = (insn & 0x000001ff ); 
+          codestream_get ();
+
+         /* this sequence is used to get the address of the return
+          * buffer for a function that returns a structure
+          */
+         insn = codestream_peek ();
+         if (insn & OPMASK == 0x60000000)
+           codestream_get ();
+
+         return n;
+       }
+      else
+       {
+         return (0);
+       }
+    }
+  return (-1);
+}
+
+/* return pc of first real instruction */
+CORE_ADDR
+skip_prologue (pc)
+     int pc;
+{
+  unsigned int insn;
+  int i;
+  CORE_ADDR pos;
+  
+  if (arc_get_frame_setup (pc) < 0)
+    return (pc);
+  
+  /* skip over register saves */
+  for (i = 0; i < 10; i++)
+    {
+      insn = codestream_peek ();
+      if (insn & OPMASK != 0x10000000)       /* break if not st inst */
+       break;
+      codestream_get ();
+    }
+     
+  codestream_seek (pos);
+  return (codestream_tell ());
+}
+
+/* Return number of args passed to a frame.
+   Can return -1, meaning no way to tell.  */
+int
+frame_num_args (fi)
+     struct frame_info *fi;
+{
+#if 1
+  return -1;
+#else
+  /* This loses because not only might the compiler not be popping the
+     args right after the function call, it might be popping args from both
+     this call and a previous one, and we would say there are more args
+     than there really are.  Is it true for ARC */
+
+  int retpc;                                           
+  unsigned char op;                                    
+  struct frame_info *pfi;
+
+  int frameless;
+
+  FRAMELESS_FUNCTION_INVOCATION (fi, frameless);
+  if (frameless)
+    /* In the absence of a frame pointer, GDB doesn't get correct values
+       for nameless arguments.  Return -1, so it doesn't print any
+       nameless arguments.  */
+    return -1;
+
+  pfi = get_prev_frame_info (fi);                      
+  if (pfi == 0)
+    {
+      /* Note:  this can happen if we are looking at the frame for
+        main, because FRAME_CHAIN_VALID won't let us go into
+        start.  If we have debugging symbols, that's not really
+        a big deal; it just means it will only show as many arguments
+        to main as are declared.  */
+      return -1;
+    }
+  else
+    {
+      retpc = pfi->pc;                                 
+      op = read_memory_integer (retpc, 1);                     
+      if (op == 0x59)                                  
+       /* pop %ecx */                         
+       return 1;                               
+       }
+      else
+       {
+         return 0;
+       }
+    }
+#endif
+}
+
+/*
+ * parse the first few instructions of the function to see
+ * what registers were stored.
+ *
+ * The startup sequence can be at the start of the function.
+ * 'st fp,[sp], st blink,[sp+4], mov fp,sp' 
+ *
+ * Local space is allocated just below by sub sp,nnn,sp
+ * Next, the registers used by this function are stored.
+ */
+
+void
+frame_find_saved_regs (fip, fsrp)
+     struct frame_info *fip;
+     struct frame_saved_regs *fsrp;
+{
+  long locals;
+  unsigned int insn;
+  CORE_ADDR dummy_bottom;
+  CORE_ADDR adr;
+  int i, regnum, offset;
+  
+  memset (fsrp, 0, sizeof *fsrp);
+  
+  /* if frame is the end of a dummy, compute where the
+   * beginning would be
+   */
+  dummy_bottom = fip->frame - 4 - REGISTER_BYTES - CALL_DUMMY_LENGTH;
+  
+  /* check if the PC is in the stack, in a dummy frame */
+  if (dummy_bottom <= fip->pc && fip->pc <= fip->frame) 
+    {
+      /* all regs were saved by push_call_dummy () */
+      adr = fip->frame;
+      for (i = 0; i < NUM_REGS; i++) 
+       {
+         adr -= REGISTER_RAW_SIZE (i);
+         fsrp->regs[i] = adr;
+       }
+      return;
+    }
+  
+  locals = arc_get_frame_setup (get_pc_function_start (fip->pc));
+  
+  if (locals >= 0) 
+    {
+      adr = fip->frame - locals;
+      for (i = 0; i < 10; i++) 
+       {
+         insn = codestream_get ();
+         if (insn & 0xffff8000 != 0x100d8000)
+           break;
+          regnum = (insn & 0x00007c00) >> 9;
+         offset = (insn << 23) >> 23;
+         fsrp->regs[regnum] = adr + offset;
+       }
+    }
+  
+  fsrp->regs[PC_REGNUM] = fip->frame + 4;
+  fsrp->regs[FP_REGNUM] = fip->frame;
+}
+
+void
+push_dummy_frame ()
+{
+  CORE_ADDR sp = read_register (SP_REGNUM);
+  int regnum;
+  char regbuf[MAX_REGISTER_RAW_SIZE];
+
+  read_register_gen (PC_REGNUM, regbuf);
+  write_memory (sp+4, regbuf, REGISTER_SIZE);
+  read_register_gen (FP_REGNUM, regbuf);
+  write_memory (sp, regbuf, REGISTER_SIZE);
+  write_register (FP_REGNUM, sp);
+  for (regnum = 0; regnum < NUM_REGS; regnum++)
+    {
+      read_register_gen (regnum, regbuf);
+      sp = push_bytes (sp, regbuf, REGISTER_RAW_SIZE (regnum));
+    }
+  sp += (2*REGISTER_SIZE);
+  write_register (SP_REGNUM, sp);
+}
+
+void
+pop_frame ()
+{
+  struct frame_info *frame = get_current_frame ();
+  CORE_ADDR fp;
+  int regnum;
+  struct frame_saved_regs fsr;
+  char regbuf[MAX_REGISTER_RAW_SIZE];
+  
+  fp = FRAME_FP (frame);
+  get_frame_saved_regs (frame, &fsr);
+  for (regnum = 0; regnum < NUM_REGS; regnum++) 
+    {
+      CORE_ADDR adr;
+      adr = fsr.regs[regnum];
+      if (adr)
+       {
+         read_memory (adr, regbuf, REGISTER_RAW_SIZE (regnum));
+         write_register_bytes (REGISTER_BYTE (regnum), regbuf,
+                               REGISTER_RAW_SIZE (regnum));
+       }
+    }
+  write_register (FP_REGNUM, read_memory_integer (fp, 4));
+  write_register (PC_REGNUM, read_memory_integer (fp + 4, 4));
+  write_register (SP_REGNUM, fp + 8);
+  flush_cached_frames ();
+}
+
+#ifdef GET_LONGJMP_TARGET
+/* Figure out where the longjmp will land.  Slurp the args out of the stack.
+   We expect the first arg to be a pointer to the jmp_buf structure from which
+   we extract the pc (JB_PC) that we will land at.  The pc is copied into PC.
+   This routine returns true on success. */
+
+int
+get_longjmp_target(pc)
+     CORE_ADDR *pc;
+{
+  char buf[TARGET_PTR_BIT / TARGET_CHAR_BIT];
+  CORE_ADDR sp, jb_addr;
+
+  sp = read_register (SP_REGNUM);
+
+  if (target_read_memory (sp + SP_ARG0, /* Offset of first arg on stack */
+                         buf,
+                         TARGET_PTR_BIT / TARGET_CHAR_BIT))
+    return 0;
+
+  jb_addr = extract_address (buf, TARGET_PTR_BIT / TARGET_CHAR_BIT);
+
+  if (target_read_memory (jb_addr + JB_PC * JB_ELEMENT_SIZE, buf,
+                         TARGET_PTR_BIT / TARGET_CHAR_BIT))
+    return 0;
+
+  *pc = extract_address (buf, TARGET_PTR_BIT / TARGET_CHAR_BIT);
+
+  return 1;
+}
+#endif /* GET_LONGJMP_TARGET */
+
+void _initialize_arc_tdep ()
+{
+  tm_print_insn = print_insn_arc;
+}
diff --git a/gdb/remote-arc.c b/gdb/remote-arc.c
new file mode 100644 (file)
index 0000000..98e7a57
--- /dev/null
@@ -0,0 +1,1012 @@
+/* Remote target communications for serial-line targets in custom GDB protocol
+   Copyright 1988, 1991, 1992, 1993, 1994 Free Software Foundation, Inc.
+
+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., 675 Mass Ave, Cambridge, MA 02139, USA.  */
+
+#include "defs.h"
+#include <string.h>
+#include <fcntl.h>
+#include "frame.h"
+#include "inferior.h"
+#include "bfd.h"
+#include "symfile.h"
+#include "target.h"
+#include "wait.h"
+#include "terminal.h"
+#include "gdbcmd.h"
+#include "objfiles.h"
+#include "gdb-stabs.h"
+
+#include "dcache.h"
+
+#ifdef USG
+#include <sys/types.h>
+#endif
+
+#include <signal.h>
+#include "serial.h"
+
+/* Prototypes for local functions */
+
+static int
+arc_write_bytes PARAMS ((CORE_ADDR memaddr, unsigned char *myaddr, int len));
+
+static int
+arc_read_bytes PARAMS ((CORE_ADDR memaddr, unsigned char *myaddr, int len));
+
+static void
+arc_files_info PARAMS ((struct target_ops *ignore));
+
+static int
+arc_xfer_memory PARAMS ((CORE_ADDR memaddr, char *myaddr, int len,
+                           int should_write, struct target_ops *target));
+
+static void
+arc_xfer_cntlreg PARAMS ((int rw, unsigned char *data));
+
+static void 
+arc_prepare_to_store PARAMS ((void));
+
+static void
+arc_fetch_registers PARAMS ((int regno));
+
+static void
+arc_resume PARAMS ((int pid, int step, enum target_signal siggnal));
+
+static int
+arc_start_remote PARAMS ((char *dummy));
+
+static void
+arc_open PARAMS ((char *name, int from_tty));
+
+static void
+arc_close PARAMS ((int quitting));
+
+static void
+arc_store_registers PARAMS ((int regno));
+
+static void
+getpkt PARAMS ((char *buf, int len));
+
+static int
+putpkt PARAMS ((char *buf, int len));
+
+static int arc_wait PARAMS ((int pid, struct target_waitstatus *status));
+
+static void
+arc_detach PARAMS ((char *args, int from_tty));
+
+static void
+arc_interrupt PARAMS ((int signo));
+
+static void
+arc_interrupt_twice PARAMS ((int signo));
+
+static void
+interrupt_query PARAMS ((void));
+
+extern struct target_ops arc_ops;      /* Forward decl */
+
+static int aux_reg_map[3][30] = AUX_REG_MAP;
+
+/* This was 5 seconds, which is a long time to sit and wait.
+   Unless this is going though some terminal server or multiplexer or
+   other form of hairy serial connection, I would think 2 seconds would
+   be plenty.  */
+static int remote_timeout = 2;
+
+#if 0
+int icache;
+#endif
+
+/* Descriptor for I/O to remote machine.  Initialize it to NULL so that
+   arc_open knows that we don't have a file open when the program
+   starts.  */
+static serial_t arc_desc = NULL;
+
+#define AUDIO_PROCESSOR   0
+#define GRAPHIC_PROCESSOR 1
+#define HOST_PROCESSOR    2
+static unsigned char cntl_reg_halt_bit[3] = { 0x08, 0x10, 0x20 };
+static unsigned char cntl_reg_step_bit[3] = { 0x01, 0x02, 0x04 };
+
+static int curr_processor = HOST_PROCESSOR;
+static unsigned char cntl_reg = 0;
+static unsigned int status_reg = 0;
+
+#define        PBUFSIZ 32
+#define MAXBUFBYTES 32
+
+\f
+/* Clean up connection to a remote debugger.  */
+
+/* ARGSUSED */
+static void
+arc_close (quitting)
+     int quitting;
+{
+  if (arc_desc)
+    SERIAL_CLOSE (arc_desc);
+  arc_desc = NULL;
+}
+
+static int
+arc_start_remote (dummy)
+     char *dummy;
+{
+  immediate_quit = 1;          /* Allow user to interrupt it */
+  arc_xfer_cntlreg (1, &cntl_reg);
+  immediate_quit = 0;
+
+  start_remote ();             /* Initialize gdb process mechanisms */
+  return 1;
+}
+
+/* Open a connection to a remote debugger.
+   NAME is the filename used for communication.  */
+
+static DCACHE *remote_dcache;
+
+static void
+arc_open (name, from_tty)
+     char *name;
+     int from_tty;
+{
+  if (name == 0)
+    error (
+"To open a arc debug connection, you need to specify what parallel\n\
+device is attached to the remote system.");
+
+  target_preopen (from_tty);
+
+  unpush_target (&arc_ops);
+
+  remote_dcache = dcache_init (arc_read_bytes, arc_write_bytes);
+
+  arc_desc = SERIAL_OPEN (name);
+  if (!arc_desc)
+    perror_with_name (name);
+
+  /* If there is something sitting in the buffer we might take it as a
+     response to a command, which would be bad.  */
+  SERIAL_FLUSH_INPUT (arc_desc);
+
+  if (from_tty)
+    {
+      puts_filtered ("Remote debugging using ");
+      puts_filtered (name);
+      puts_filtered ("\n");
+    }
+  push_target (&arc_ops);      /* Switch to using remote target now */
+
+  /* Without this, some commands which require an active target (such as kill)
+     won't work.  This variable serves (at least) double duty as both the pid
+     of the target process (if it has such), and as a flag indicating that a
+     target is active.  These functions should be split out into seperate
+     variables, especially since GDB will someday have a notion of debugging
+     several processes.  */
+
+  inferior_pid = 42000;
+
+  /* Start the remote connection; if error (0), discard this target.
+     In particular, if the user quits, be sure to discard it
+     (we'd be in an inconsistent state otherwise).  */
+  if (!catch_errors (arc_start_remote, (char *)0, 
+       "Couldn't establish connection to remote target\n", RETURN_MASK_ALL))
+    pop_target();
+}
+
+/* arc_detach()
+   takes a program previously attached to and detaches it.
+   We better not have left any breakpoints
+   in the program or it'll die when it hits one.
+   Close the open connection to the remote debugger.
+   Use this when you want to detach and do something else
+   with your gdb.  */
+
+static void
+arc_detach (args, from_tty)
+     char *args;
+     int from_tty;
+{
+  if (args)
+    error ("Argument given to \"detach\" when remotely debugging.");
+  
+  pop_target ();
+  if (from_tty)
+    puts_filtered ("Ending remote debugging.\n");
+}
+
+/* arc_set_addrreg
+   set addr reg from debug system. */
+
+static void
+arc_set_addrreg (addr)
+     unsigned int addr;
+{
+  unsigned char buf[6] = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0};
+
+  buf[0] = 0x0;
+  buf[4] = addr & 0xff;
+  buf[3] = (addr >> 8) & 0xff;
+  buf[2] = (addr >> 16) & 0xff;
+  buf[1] = (addr >> 24) & 0xff;
+
+  putpkt (buf, 5);
+}
+
+/* arc_xfer_datareg
+   read or write data reg from debug system. */
+
+static void
+arc_xfer_datareg (rw, aux, incr, data)
+     int rw;
+     int aux;
+     int incr;
+     unsigned int *data;
+{
+  unsigned char buf1[6] = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0};
+  unsigned char buf2[5];
+  unsigned int tmp;
+
+  /* read or write data */
+  buf1[0] = (aux ? 0x81 : 0x41);
+  if (incr)
+    buf1[0] += 0x20;
+  if (rw)    /* read */
+    {
+      buf1[0] += 0x10;
+      putpkt (buf1, 1);
+      getpkt (buf2, 4);
+      *data = *(int *)buf2;
+    }
+  else
+    {
+      tmp = *data;
+      buf1[4] = tmp & 0xff;
+      buf1[3] = (tmp >> 8) & 0xff;
+      buf1[2] = (tmp >> 16) & 0xff;
+      buf1[1] = (tmp >> 24) & 0xff;
+      putpkt (buf1, 5);
+    }
+}
+
+/* arc_xfer_cntlreg
+   read or write control reg from debug system. */
+
+static void
+arc_xfer_cntlreg (rw, data)
+     int rw;
+     unsigned char *data;
+{
+  unsigned char buf[3] = {0x0, 0x0, 0x0};
+
+  if (rw)
+    {
+      buf[0] = 0x12;
+      putpkt (buf, 1);
+      getpkt (data, 1);
+    }
+  else
+    {
+      buf[0] = 0x02;
+      buf[1] = *data;
+      putpkt (buf, 2);
+    }
+}
+
+/* arc_xfer_reg
+   read or write a reg to arc processors. */
+
+static void
+arc_xfer_reg (processor, rw, aux, regnum, data)
+     int processor;
+     int rw;
+     int aux;
+     int regnum;
+     unsigned int *data;
+{
+  unsigned int tmp;
+
+  if (processor == HOST_PROCESSOR)
+    {
+      /* write addr (regnum) */
+      arc_set_addrreg (regnum);
+      arc_xfer_datareg (rw, aux, 0, data);
+    }
+  else
+    {
+      /* write addr register (aux r14) */
+      arc_set_addrreg (0xe);
+      tmp = aux ? (regnum | 0x80000000) : regnum;
+      arc_xfer_datareg (0, 1, 0, tmp);
+
+      /* read/write from data reg (aux reg 15/16) */
+      arc_set_addrreg (processor == AUDIO_PROCESSOR ? 0x10 : 0xf);
+      arc_xfer_datareg (rw, 1, 0, data);
+    }
+}
+
+\f
+/* Tell the remote machine to resume.  */
+
+static enum target_signal last_sent_signal = TARGET_SIGNAL_0;
+int last_sent_step;
+
+static void
+arc_resume (pid, step, siggnal)
+     int pid, step;
+     enum target_signal siggnal;
+{
+  unsigned int tmp;
+
+  dcache_flush (remote_dcache);
+
+  last_sent_signal = siggnal;
+  last_sent_step = step;
+
+  /* don't know how to handle signal in ARC ***
+  if (siggnal != TARGET_SIGNAL_0)
+    {
+      buf[0] = step ? 'S' : 'C';
+      buf[1] = tohex (((int)siggnal >> 4) & 0xf);
+      buf[2] = tohex ((int)siggnal & 0xf);
+      buf[3] = '\0';
+    }
+  */
+
+  if (step)
+    {
+      /* write the step bit in control reg of debug system */
+      unsigned char tmp_char = cntl_reg | cntl_reg_step_bit[curr_processor];
+      arc_xfer_cntlreg (0, &tmp_char);
+    }
+  else
+    {
+      /* clear the halt bit in the status register */
+      tmp = status_reg | 0x02000000;
+      arc_xfer_reg (curr_processor, 0, 1, 0, &tmp);
+    }
+}
+
+\f
+static void (*ofunc)();
+
+/* Send to target to halt it. */
+static void
+arc_interrupt (signo)
+     int signo;
+{
+  unsigned char buf[3] = {0x02, 0x0, 0x0};
+  /* If this doesn't work, try more severe steps.  */
+  signal (signo, arc_interrupt_twice);
+  
+  if (remote_debug)
+    printf_unfiltered ("arc_interrupt called\n");
+
+  if (cntl_reg & cntl_reg_halt_bit[curr_processor]) 
+    return;
+
+  buf[1] = cntl_reg | cntl_reg_halt_bit[curr_processor];
+  putpkt (buf, 2);
+  return;  
+}
+
+/* The user typed ^C twice.  */
+static void
+arc_interrupt_twice (signo)
+     int signo;
+{
+  signal (signo, ofunc);
+  
+  interrupt_query ();
+
+  signal (signo, arc_interrupt);
+}
+
+/* Ask the user what to do when an interrupt is received.  */
+
+static void
+interrupt_query ()
+{
+  target_terminal_ours ();
+
+  if (query ("Interrupted while waiting for the program.\n\
+Give up (and stop debugging it)? "))
+    {
+      target_mourn_inferior ();
+      return_to_top_level (RETURN_QUIT);
+    }
+
+  target_terminal_inferior ();
+}
+
+/* If nonzero, ignore the next kill.  */
+int kill_kludge;
+
+/* Wait until the remote machine stops, then return,
+   storing status in STATUS just as `wait' would.
+   Returns "pid" (though it's not clear what, if anything, that
+   means in the case of this target).  */
+
+static int
+arc_wait (pid, status)
+     int pid;
+     struct target_waitstatus *status;
+{
+  unsigned char buf[PBUFSIZ];
+  int thread_num = -1;
+
+  status->kind = TARGET_WAITKIND_EXITED;
+  status->value.integer = 0;
+
+  while (1)
+    {
+      unsigned char *p;
+
+      ofunc = (void (*)()) signal (SIGINT, arc_interrupt);
+      arc_xfer_cntlreg (1, &cntl_reg);
+      signal (SIGINT, ofunc);
+      if (cntl_reg & cntl_reg_halt_bit[curr_processor])
+       break;
+      status->kind = TARGET_WAITKIND_STOPPED;
+    }
+  arc_xfer_reg (curr_processor, 1, 1, 0, &status_reg);
+
+  return inferior_pid;
+}
+
+/* Number of bytes of registers this implements.  */
+static int register_bytes_found;
+
+/* Read the remote registers into the block REGS.  */
+/* Currently we just read all the registers, so we don't use regno.  */
+
+static void
+arc_fetch_registers (regno)
+     int regno;
+{
+  int i;
+  char regs[REGISTER_BYTES];
+
+  /* Unimplemented registers read as all bits zero.  */
+  memset (regs, 0, REGISTER_BYTES);
+
+  /* get core register */
+  arc_set_addrreg (0);
+  for (i = 0; i < AUX_BEG_REGNUM; i++)
+    {
+      if (curr_processor == HOST_PROCESSOR)
+       arc_xfer_datareg (1, 0, 1, &regs[REGISTER_BYTE(i)]);
+      else
+       arc_xfer_reg (curr_processor, 1, 0, regno, &regs[REGISTER_BYTE(i)]);
+    }
+
+  /* get aux register */
+  for (i = AUX_BEG_REGNUM; i < AUX_END_REGNUM; i++)
+    {
+      int auxregnum = aux_reg_map[curr_processor][i-AUX_BEG_REGNUM+1];
+      if (auxregnum == -1)
+       continue;
+      arc_xfer_reg (curr_processor, 1, 1, auxregnum, &regs[REGISTER_BYTE(i)]);
+    }
+  /* copy from status register to pc */
+  for (i = 1; i <= 3; i++)
+    registers[REGISTER_BYTE (PC_REGNUM)+i] = 
+      registers[REGISTER_BYTE (STA_REGNUM)+i];
+
+/*
+  if (i != register_bytes_found)
+    {
+      register_bytes_found = i;
+      if (!REGISTER_BYTES_OK (i))
+       warning ("Remote reply is too short: %s", buf);
+    }
+*/
+
+  for (i = 0; i < NUM_REGS; i++)
+    supply_register (i, &regs[REGISTER_BYTE(i)]);
+}
+
+/* Prepare to store registers.  Since we may send them all,
+   we have to read out the ones we don't want to change first.  */
+
+static void 
+arc_prepare_to_store ()
+{
+  /* Make sure the entire registers array is valid.  */
+  read_register_bytes (0, (char *)NULL, REGISTER_BYTES);
+}
+
+/* Store register REGNO, or all registers if REGNO == -1, from the contents
+   of REGISTERS.  FIXME: ignores errors.  */
+
+static void
+arc_store_registers (regno)
+     int regno;
+{
+  int i;
+  char *regp;
+
+  /* Try storing a single register.  */
+  if (regno >= 0)
+    {
+      int isaux = (regno >= AUX_BEG_REGNUM ? 1 : 0);
+
+      regp = &registers[REGISTER_BYTE (regno)];
+      arc_xfer_reg (curr_processor, 0, isaux, regno, regp);
+    }
+
+  /* store core regs */
+  arc_set_addrreg (0);
+  for (i = 0; i < AUX_BEG_REGNUM; i++)
+    {
+      regp = &registers[REGISTER_BYTE (i)];
+      if (curr_processor == HOST_PROCESSOR)
+       arc_xfer_datareg (0, 0, 1, regp);
+      else
+       arc_xfer_reg (curr_processor, 0, 0, regno, regp);
+    }
+
+  /* store aux regs */
+  /* copy pc back to status register */
+  for (i = 1; i <= 3; i++)
+    registers[REGISTER_BYTE (STA_REGNUM)+i] = 
+      registers[REGISTER_BYTE (PC_REGNUM)+i];
+  for (i = AUX_BEG_REGNUM; i <= AUX_END_REGNUM; i++)
+    {
+      int auxregnum = aux_reg_map[curr_processor][i-AUX_BEG_REGNUM+1];
+      if (auxregnum == -1)
+       continue;
+      regp = &registers[REGISTER_BYTE (i)];
+      arc_xfer_reg (curr_processor, 0, 1, auxregnum, regp);
+    }
+
+}
+
+#if 0
+/* Use of the data cache is disabled because it loses for looking at
+   and changing hardware I/O ports and the like.  Accepting `voltile'
+   would perhaps be one way to fix it, but a better way which would
+   win for more cases would be to use the executable file for the text
+   segment, like the `icache' code below but done cleanly (in some
+   target-independent place, perhaps in target_xfer_memory, perhaps
+   based on assigning each target a speed or perhaps by some simpler
+   mechanism).  */
+
+/* Read a word from remote address ADDR and return it.
+   This goes through the data cache.  */
+
+static int
+arc_fetch_word (addr)
+     CORE_ADDR addr;
+{
+#if 0
+  if (icache)
+    {
+      extern CORE_ADDR text_start, text_end;
+
+      if (addr >= text_start && addr < text_end)
+       {
+         int buffer;
+         xfer_core_file (addr, &buffer, sizeof (int));
+         return buffer;
+       }
+    }
+#endif
+  return dcache_fetch (remote_dcache, addr);
+}
+
+/* Write a word WORD into remote address ADDR.
+   This goes through the data cache.  */
+
+static void
+arc_store_word (addr, word)
+     CORE_ADDR addr;
+     int word;
+{
+  dcache_poke (remote_dcache, addr, word);
+}
+#endif /* 0 */
+
+\f
+/* Write memory data directly to the remote machine.
+   This does not inform the data cache; the data cache uses this.
+   MEMADDR is the address in the remote memory space.
+   MYADDR is the address of the buffer in our space.
+   LEN is the number of bytes.
+
+   Returns number of bytes transferred, or 0 for error.  */
+
+static int
+arc_write_bytes (memaddr, myaddr, len)
+     CORE_ADDR memaddr;
+     unsigned char *myaddr;
+     int len;
+{
+  char buf1[6] = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0};
+  char buf2[6] = {0x21, 0x0, 0x0, 0x0, 0x0, 0x0};
+  int i;
+  char *p;
+
+  /* We send target system values byte by byte, in increasing byte addresses*/
+
+  buf1[4] = memaddr & 0xff;
+  buf1[3] = (memaddr >> 8) & 0xff;
+  buf1[2] = (memaddr >> 16) & 0xff;
+  buf1[1] = (memaddr >> 24) & 0xff;
+  putpkt (buf1, 5);
+
+  for (i = 0; i < len; )
+    {
+      buf2[1] = myaddr[i++];
+      buf2[2] = myaddr[i++];
+      buf2[3] = myaddr[i++];
+      buf2[4] = myaddr[i++];
+      putpkt (buf2, 5);
+    }
+
+  return len;
+}
+
+/* Read memory data directly from the remote machine.
+   This does not use the data cache; the data cache uses this.
+   MEMADDR is the address in the remote memory space.
+   MYADDR is the address of the buffer in our space.
+   LEN is the number of bytes.
+
+   Returns number of bytes transferred, or 0 for error.  */
+
+static int
+arc_read_bytes (memaddr, myaddr, len)
+     CORE_ADDR memaddr;
+     unsigned char *myaddr;
+     int len;
+{
+  unsigned char buf1[6] = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0};
+  unsigned char buf2[3] = {0x31, 0x0, 0x0};
+  unsigned char buf3[5];
+  int i;
+  char *p;
+
+  /* We send target system values byte by byte, in increasing byte addresses*/
+
+  buf1[4] = memaddr & 0xff;
+  buf1[3] = (memaddr >> 8) & 0xff;
+  buf1[2] = (memaddr >> 16) & 0xff;
+  buf1[1] = (memaddr >> 24) & 0xff;
+  putpkt (buf1, 5);
+
+  for (i = 0; i < len; )
+    {
+      putpkt (buf2, 2);
+      getpkt (buf3, 4);
+      myaddr[i++] = buf3[1];
+      myaddr[i++] = buf3[2];
+      myaddr[i++] = buf3[3];
+      myaddr[i++] = buf3[4];
+    }
+
+  return i;
+}
+
+\f
+/* Read or write LEN bytes from inferior memory at MEMADDR, transferring
+   to or from debugger address MYADDR.  Write to inferior if SHOULD_WRITE is
+   nonzero.  Returns length of data written or read; 0 for error.  */
+
+/* ARGSUSED */
+static int
+arc_xfer_memory(memaddr, myaddr, len, should_write, target)
+     CORE_ADDR memaddr;
+     char *myaddr;
+     int len;
+     int should_write;
+     struct target_ops *target;                        /* ignored */
+{
+  int xfersize;
+  int bytes_xferred;
+  int total_xferred = 0;
+
+  while (len > 0)
+    {
+      if (len > MAXBUFBYTES)
+       xfersize = MAXBUFBYTES;
+      else
+       xfersize = len;
+
+      if (should_write)
+        bytes_xferred = arc_write_bytes (memaddr,
+                                           (unsigned char *)myaddr, xfersize);
+      else
+       bytes_xferred = arc_read_bytes (memaddr,
+                                          (unsigned char *)myaddr, xfersize);
+
+      /* If we get an error, we are done xferring.  */
+      if (bytes_xferred == 0)
+       break;
+
+      memaddr += bytes_xferred;
+      myaddr  += bytes_xferred;
+      len     -= bytes_xferred;
+      total_xferred += bytes_xferred;
+    }
+  return total_xferred;
+}
+
+\f
+static void
+arc_files_info (ignore)
+     struct target_ops *ignore;
+{
+  puts_filtered ("Debugging a target over a serial line.\n");
+}
+\f
+
+/* Read a single character from the remote end, masking it down to 7 bits. */
+static int
+readchar ()
+{
+  int ch;
+
+  ch = SERIAL_READCHAR (arc_desc, 0);
+
+  switch (ch)
+    {
+    case SERIAL_EOF:
+      error ("Remote connection closed");
+    case SERIAL_ERROR:
+      perror_with_name ("Remote communication error");
+    case SERIAL_TIMEOUT:
+      return ch;
+    default:
+      return ch & 0x7f;
+    }
+}
+
+/* Send a packet to the remote machine, with error checking.
+   The data of the packet is in BUF.  */
+
+static int
+putpkt (buf, len)
+     char *buf;
+     int len;
+{
+  int i = 0;
+  unsigned char csum = 0;
+
+  while (i < len)
+    {
+      if (remote_debug)
+       {
+         printf_unfiltered ("Sending packet: %s...", buf);
+         gdb_flush(gdb_stdout);
+       }
+      if (SERIAL_WRITE (arc_desc, buf, i))
+       perror_with_name ("putpkt: write failed");
+      i++;
+
+#if 0
+      /* This is wrong.  If doing a long backtrace, the user should be
+        able to get out next time we call QUIT, without anything as violent
+        as interrupt_query.  If we want to provide a way out of here
+        without getting to the next QUIT, it should be based on hitting
+        ^C twice as in arc_wait.  */
+      if (quit_flag)
+       {
+         quit_flag = 0;
+         interrupt_query ();
+       }
+#endif
+    }
+}
+
+/* Read a packet from the remote machine, with error checking,
+   and store it in BUF.  BUF is expected to be of size PBUFSIZ.
+   If FOREVER, wait forever rather than timing out; this is used
+   while the target is executing user code.  */
+
+static void
+getpkt (buf, len)
+     char *buf;
+     int len;
+{
+  int c;
+  int i;
+
+  for (i = 0; i < len; i++)
+    {
+      c = readchar ();
+      if (c == SERIAL_ERROR)
+       {
+         if (remote_debug)
+           puts_filtered ("Read error.\n");
+       }
+    }
+  buf[i] = '\0';
+
+  if (remote_debug)
+    fprintf_unfiltered (gdb_stderr, "Packet received: %s\n", buf);
+  if (i != len)
+    printf_unfiltered ("Ignoring packet error, continuing...\n");
+}
+\f
+static void
+arc_kill ()
+{
+  /* For some mysterious reason, wait_for_inferior calls kill instead of
+     mourn after it gets TARGET_WAITKIND_SIGNALLED.  Work around it.  */
+  if (kill_kludge)
+    {
+      kill_kludge = 0;
+      target_mourn_inferior ();
+      return;
+    }
+  target_mourn_inferior ();
+}
+
+static void
+arc_mourn ()
+{
+  unpush_target (&arc_ops);
+  generic_mourn_inferior ();
+}
+
+\f
+#ifdef REMOTE_BREAKPOINT
+
+/* On some machines, e.g. 68k, we may use a different breakpoint instruction
+   than other targets.  */
+static unsigned char break_insn[] = REMOTE_BREAKPOINT;
+
+/* Check that it fits in BREAKPOINT_MAX bytes.  */
+static unsigned char check_break_insn_size[BREAKPOINT_MAX] = REMOTE_BREAKPOINT;
+
+#else /* No REMOTE_BREAKPOINT.  */
+
+static unsigned char break_insn[] = BREAKPOINT;
+
+#endif /* No REMOTE_BREAKPOINT.  */
+
+/* Insert a breakpoint on targets that don't have any better breakpoint
+   support.  We read the contents of the target location and stash it,
+   then overwrite it with a breakpoint instruction.  ADDR is the target
+   location in the target machine.  CONTENTS_CACHE is a pointer to 
+   memory allocated for saving the target contents.  It is guaranteed
+   by the caller to be long enough to save sizeof BREAKPOINT bytes (this
+   is accomplished via BREAKPOINT_MAX).  */
+
+static int
+arc_insert_breakpoint (addr, contents_cache)
+     CORE_ADDR addr;
+     char *contents_cache;
+{
+  int val;
+
+  val = target_read_memory (addr, contents_cache, sizeof break_insn);
+  if (val == 0)
+    val = target_write_memory (addr, (char *)break_insn, sizeof break_insn);
+  return val;
+}
+
+static int
+arc_remove_breakpoint (addr, contents_cache)
+     CORE_ADDR addr;
+     char *contents_cache;
+{
+  return target_write_memory (addr, contents_cache, sizeof break_insn);
+}
+
+/* switch_command
+   support 'switch' command to switch among the three processors of ARC. */
+
+static void
+switch_command (args, fromtty)
+     char *args;
+     int fromtty;
+{
+  unsigned char cmd;
+  int proc;
+  struct target_waitstatus status;
+
+  if (strncmp (args, "audio", 3) == 0)
+    proc = 0;
+  else if (strncmp (args, "graphic", 3) == 0)
+    proc = 1;
+  else if (strncmp (args, "host", 4) == 0)
+    proc = 2;
+
+  if (cntl_reg & cntl_reg_halt_bit[proc])
+    {
+      curr_processor = proc;
+      return;
+    }
+
+  if ((proc != HOST_PROCESSOR) && 
+      !(cntl_reg & cntl_reg_halt_bit[HOST_PROCESSOR]))
+    {
+      cmd = cntl_reg | cntl_reg_halt_bit[HOST_PROCESSOR];
+      arc_xfer_cntlreg (0, &cmd);
+      curr_processor = HOST_PROCESSOR;
+      arc_wait (inferior_pid, &status);
+    }
+  if (!(cntl_reg & cntl_reg_halt_bit[proc]))
+    {
+      cmd = cntl_reg | cntl_reg_halt_bit[proc];
+      arc_xfer_cntlreg (0, &cmd);
+      curr_processor = proc;
+      arc_wait (inferior_pid, &status);
+      arc_fetch_registers (-1);
+    }
+}
+
+\f
+/* Define the target subroutine names */
+
+struct target_ops arc_ops = {
+  "arc",                       /* to_shortname */
+  "Remote target in arc-specific protocol",    /* to_longname */
+  "Use a remote computer via a parallel line, using a arc-specific protocol.\n\
+Specify the device it is connected to.",  /* to_doc */
+  arc_open,                    /* to_open */
+  arc_close,                   /* to_close */
+  NULL,                                /* to_attach */
+  arc_detach,          /* to_detach */
+  arc_resume,          /* to_resume */
+  arc_wait,                    /* to_wait */
+  arc_fetch_registers, /* to_fetch_registers */
+  arc_store_registers, /* to_store_registers */
+  arc_prepare_to_store,        /* to_prepare_to_store */
+  arc_xfer_memory,             /* to_xfer_memory */
+  arc_files_info,              /* to_files_info */
+
+  arc_insert_breakpoint,       /* to_insert_breakpoint */
+  arc_remove_breakpoint,       /* to_remove_breakpoint */
+
+  NULL,                                /* to_terminal_init */
+  NULL,                                /* to_terminal_inferior */
+  NULL,                                /* to_terminal_ours_for_output */
+  NULL,                                /* to_terminal_ours */
+  NULL,                                /* to_terminal_info */
+  arc_kill,                    /* to_kill */
+  generic_load,                        /* to_load */
+  NULL,                                /* to_lookup_symbol */
+  NULL,                                /* to_create_inferior */
+  arc_mourn,                   /* to_mourn_inferior */
+  0,                           /* to_can_run */
+  0,                           /* to_notice_signals */
+  0,                           /* to_stop */
+  process_stratum,             /* to_stratum */
+  NULL,                                /* to_next */
+  1,                           /* to_has_all_memory */
+  1,                           /* to_has_memory */
+  1,                           /* to_has_stack */
+  1,                           /* to_has_registers */
+  1,                           /* to_has_execution */
+  NULL,                                /* sections */
+  NULL,                                /* sections_end */
+  OPS_MAGIC                    /* to_magic */
+};
+
+void
+_initialize_remote_arc ()
+{
+  add_target (&arc_ops);
+  add_com ("switch <processor>", class_obscure, switch_command,
+          "Switch to debug a different processor, can be one of 'host', \
+'graphic' and 'audio'.");
+}
diff --git a/gdb/ser-go32-para.c b/gdb/ser-go32-para.c
new file mode 100644 (file)
index 0000000..6046779
--- /dev/null
@@ -0,0 +1,261 @@
+/* Remote parallel interface for local parallel ports for GO32.
+   Copyright 1992, 1993 Free Software Foundation, Inc.
+
+   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., 675 Mass Ave, Cambridge, MA 02139, USA.  */
+
+#include "defs.h"
+#include <sys/dos.h>
+
+#if 0
+#define disable() asm("cli")
+#define enable() asm("sti")
+#endif
+
+/* this is a duumy to fill the ops structure */
+struct go32_ttystate
+  {
+    int bogus;
+  };
+
+static int go32_open PARAMS ((serial_t scb, const char *name));
+static void go32_raw PARAMS ((serial_t scb));
+static int go32_readchar PARAMS ((serial_t scb, int timeout));
+static int go32_setbaudrate PARAMS ((serial_t scb, int rate));
+static int go32_write PARAMS ((serial_t scb, const char *str, int len));
+static void go32_close PARAMS ((serial_t scb));
+static serial_ttystate go32_get_tty_state PARAMS ((serial_t scb));
+static int go32_set_tty_state PARAMS ((serial_t scb, serial_ttystate state));
+static unsigned long getivec PARAMS ((int which));
+static int dos_read PARAMS ((int fd, char *buf, int len, int timeout));
+static int dos_write PARAMS ((int fd, const char *buf, int len));
+
+#if 0
+static int iov[2];
+#define com_rb(n)      iov[n]
+#define com_tb(n)      iov[n]
+#define com_ier(n)     iov[n]+1
+#define com_ifr(n)     iov[n]+2
+#define com_bfr(n)     iov[n]+3
+#define com_mcr(n)     iov[n]+4
+#define com_lsr(n)     iov[n]+5
+#define com_msr(n)     iov[n]+6
+
+static unsigned long
+getivec (int which)
+{
+  long tryaindex;
+
+  if (GET_WORD (which * 4) != OFFSET)
+    return 0;
+
+  /* Find out where in memory this lives */
+  tryaindex = GET_WORD (which * 4 + 2) * 16 + GET_WORD (which * 4);
+
+  if (GET_WORD (tryaindex + 2) != SIGNATURE)
+    return 0;
+  if (GET_WORD (tryaindex + 4) != VERSION)
+    return 0;
+  return tryaindex;
+}
+#endif /* 0 */
+
+static int biosprn (cmd, byte, port)
+     int cmd;
+     int byte;
+     int port;
+{
+  union REGS regs;
+  regs.h.ah = cmd;
+  regs.h.al = byte;
+  regs.x.dx = port;
+  int86 (0x17, &regs, &regs);
+  return regs.h.ah;
+}
+
+static int
+dos_read (fd, buf, len)
+     int fd;
+     char *buf;
+     int len;
+{
+  int i;
+
+  for (i = 0; i < len; i++)
+    {
+      *buf++ = biosprn (2, 0, fd);
+    }
+  return len;
+}
+
+static int
+dos_write (fd, buf, len)
+     int fd;
+     const char *buf;
+     int len;
+{
+  int l;
+
+  for (l = 0; l < len; l++)
+    biosprn (0, *buf++, fd);
+
+  return len;
+}
+
+static int
+go32_open (scb, name)
+     serial_t scb;
+     const char *name;
+{
+  int port;
+
+  if (strncasecmp (name, "lpt", 3) != 0)
+    {
+      errno = ENOENT;
+      return -1;
+    }
+
+  port = name[3] - '0';
+
+  if (port != 1 && port != 2 && port != 3)
+    {
+      errno = ENOENT;
+      return -11;
+    }
+
+  return = biosprn (1, 0, port);
+  if (!return)
+    return -1;
+
+  scb->fd = port;
+  return 0;
+}
+
+static int
+go32_noop (scb)
+     serial_t scb;
+{
+  return 0;
+}
+
+static void
+go32_raw (scb)
+     serial_t scb;
+{
+  /* Always in raw mode */
+}
+
+static int
+go32_readchar (scb, timeout)
+     serial_t scb;
+     int timeout;
+{
+  char buf;
+
+  if (dos_read (scb->fd, &buf, 1))
+    return buf;
+  else
+    return SERIAL_TIMEOUT;
+}
+
+static int
+go32_write (scb, str, len)
+     serial_t scb;
+     const char *str;
+     int len;
+{
+  dos_write (scb->fd, str, len);
+  return 0;
+}
+
+/* go32_{get set}_tty_state() are both dummys to fill out the function
+   vector.  Someday, they may do something real... */
+
+static serial_ttystate
+go32_get_tty_state (scb)
+     serial_t scb;
+{
+  struct go32_ttystate *state;
+
+  state = (struct go32_ttystate *) xmalloc (sizeof *state);
+
+  return (serial_ttystate) state;
+}
+
+static int
+go32_set_tty_state (scb, ttystate)
+     serial_t scb;
+     serial_ttystate ttystate;
+{
+  return 0;
+}
+
+static int
+go32_noflush_set_tty_state (scb, new_ttystate, old_ttystate)
+     serial_t scb;
+     serial_ttystate new_ttystate;
+     serial_ttystate old_ttystate;
+{
+  return 0;
+}
+
+static void
+go32_print_tty_state (scb, ttystate)
+     serial_t scb;
+     serial_ttystate ttystate;
+{
+  /* Nothing to print.  */
+  return;
+}
+
+static int
+go32_setbaudrate (scb, rate)
+     serial_t scb;
+     int rate;
+{
+  return 0;
+}
+
+static void
+go32_close (scb)
+     serial_t scb;
+{
+}
+
+static struct serial_ops go32_ops =
+{
+  "parallel",
+  0,
+  go32_open,
+  go32_close,
+  go32_readchar,
+  go32_write,
+  go32_noop,                   /* flush output */
+  go32_noop,                   /* flush input */
+  go32_noop,                   /* send break -- currently used only for nindy */
+  go32_raw,
+  go32_get_tty_state,
+  go32_set_tty_state,
+  go32_print_tty_state,
+  go32_noflush_set_tty_state,
+  go32_setbaudrate,
+};
+
+void
+_initialize_ser_go32 ()
+{
+  serial_add_interface (&go32_ops);
+}