699 lines
17 KiB
C
699 lines
17 KiB
C
/* -*-C-*-
|
|
*******************************************************************************
|
|
*
|
|
* File: pa_stub.c
|
|
* RCS: $Header$
|
|
* Description: main routines for PA RISC monitor stub
|
|
* Author: Robert Quist
|
|
* Created: Mon Nov 1 10:00:36 1993
|
|
* Modified: Fri Nov 12 15:14:23 1993 (Robert Quist) quist@hpfcrdq
|
|
* Language: C
|
|
* Package: N/A
|
|
* Status: Experimental (Do Not Distribute)
|
|
*
|
|
*******************************************************************************
|
|
*/
|
|
|
|
/****************************************************************************
|
|
|
|
THIS SOFTWARE IS NOT COPYRIGHTED
|
|
|
|
HP offers the following for use in the public domain. HP makes no
|
|
warranty with regard to the software or its performance and the
|
|
user accepts the software "AS IS" with all faults.
|
|
|
|
HP DISCLAIMS ANY WARRANTIES, EXPRESS OR IMPLIED, WITH REGARD
|
|
TO THIS SOFTWARE INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
|
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
|
|
|
|
****************************************************************************/
|
|
|
|
/****************************************************************************
|
|
*
|
|
* Description: low level support for gdb debugger. $
|
|
*
|
|
* Considerations: only works on target hardware $
|
|
*
|
|
* NOTES: See Below $
|
|
*
|
|
* To enable debugger support, two things need to happen.
|
|
*
|
|
* One, a call to set_debug_traps() is necessary in order to allow
|
|
* any breakpoints or error conditions to be properly intercepted and
|
|
* reported to gdb.
|
|
*
|
|
* Two, a breakpoint needs to be generated to begin communication.
|
|
* This is most easily accomplished by a call to breakpoint().
|
|
* breakpoint() simulates a breakpoint
|
|
|
|
|
|
*************
|
|
*
|
|
* The following gdb commands are supported:
|
|
*
|
|
* command function Return value
|
|
*
|
|
* g return the value of the CPU registers hex data or ENN
|
|
* G set the value of the CPU registers OK or ENN
|
|
*
|
|
* mAA..AA,LLLL Read LLLL bytes at address AA..AA hex data or ENN
|
|
* MAA..AA,LLLL: Write LLLL bytes at address AA.AA OK or ENN
|
|
*
|
|
* c Resume at current address SNN ( signal NN)
|
|
* cAA..AA Continue at address AA..AA SNN
|
|
*
|
|
* s Step one instruction SNN
|
|
* sAA..AA Step one instruction from AA..AA SNN
|
|
*
|
|
* k kill
|
|
*
|
|
* ? What was the last sigval ? SNN (signal NN)
|
|
*
|
|
* bBB..BB Set baud rate to BB..BB OK or BNN, then sets
|
|
* baud rate
|
|
*
|
|
|
|
************
|
|
* All commands and responses are sent with a packet which includes a
|
|
* checksum. A packet consists of :
|
|
*
|
|
* $<packet info>#<checksum>.
|
|
*
|
|
* where
|
|
* <packet info> :: <characters representing the command or response>
|
|
* <checksum> :: < two hex digits computed as modulo 256 sum of <packetinfo>>
|
|
*
|
|
* When a packet is received, it is first acknowledged with either '+' or '-'.
|
|
* '+' indicates a successful transfer. '-' indicates a failed transfer.
|
|
*
|
|
* Example:
|
|
*
|
|
* Host: Reply:
|
|
* $m0,10#2a +$00010203040506070809101112131415#42
|
|
*
|
|
****************************************************************************/
|
|
#include <signal.h>
|
|
#include "hppa-defs.h"
|
|
|
|
/************************************************************************
|
|
*
|
|
* external low-level support
|
|
*/
|
|
#define OPT_PDC_CACHE 5
|
|
#define OPT_PDC_ADD_VALID 12
|
|
#define PGZ_MEM_PDC 0x0388 /* location of PDC_ENTRY in memory */
|
|
#define CALL_PDC (*(int (*)())((int *)(*((int *)PGZ_MEM_PDC))))
|
|
|
|
extern putDebugChar(); /* write a single character */
|
|
extern getDebugChar(); /* read and return a single char */
|
|
extern FICE(); /* flush i cache entry */
|
|
extern INLINE_BREAK(); /* break for user call */
|
|
|
|
#define RADDR_ALIGN(s,r) (s = ((unsigned int *) ((((int) r ) + 7 ) & 0xFFFFFFF8)))
|
|
|
|
/************************************************************************/
|
|
/* BUFMAX defines the maximum number of characters in inbound/outbound buffers*/
|
|
/* at least NUMREGBYTES*2 are needed for register packets */
|
|
|
|
#define BUFMAX 2048
|
|
|
|
#define NUMGPRS 32
|
|
#define NUMSRS 8
|
|
#define NUMCRS 32
|
|
#define NUMSPCLS 3
|
|
#define NUMFPRS 32
|
|
|
|
#define NUMGPRBYTES 4
|
|
#define NUMSRBYTES 4
|
|
#define NUMCRBYTES 4
|
|
#define NUMSPCLBYTES 4
|
|
#define NUMFPRBYTES 8
|
|
|
|
/* Number of bytes of registers. */
|
|
#define NUMREGBYTES \
|
|
( (NUMGPRS * NUMGPRBYTES) \
|
|
+ (NUMSRS * NUMSRBYTES) \
|
|
+ (NUMCRS * NUMCRBYTES) \
|
|
+ (NUMSPCLS * NUMSPCLBYTES) \
|
|
+ (NUMFPRS * NUMFPRBYTES) \
|
|
)
|
|
|
|
|
|
enum regnames {GR0, GR1, GR2, GR3, GR4, GR5, GR6, GR7,
|
|
GR8, GR9, GR10, GR11, GR12, GR13, GR14, GR15,
|
|
GR16, GR17, GR18, GR19, GR20, GR21, GR22, GR23,
|
|
GR24, GR25, GR26, GR27, GR28, GR29, GR30, GR31,
|
|
|
|
SR0, SR1, SR2, SR3, SR4, SR5, SR6, SR7,
|
|
|
|
CR0, CR1, CR2, CR3, CR4, CR5, CR6, CR7,
|
|
CR8, CR9, CR10, CR11, CR12, CR13, CR14, CR15,
|
|
CR16, CR17H,CR18H,CR19, CR20, CR21, CR22, CR23,
|
|
CR24, CR25, CR26, CR27, CR28, CR29, CR30, CR31,
|
|
|
|
CR17T,CR18T,CPUD0 };
|
|
|
|
enum fregnames {FPR0, FPR1, FPR2, FPR3, FPR4, FPR5, FPR6, FPR7,
|
|
FPR8, FPR9, FPR10, FPR11, FPR12, FPR13, FPR14, FPR15,
|
|
FPR16, FPR17, FPR18, FPR19, FPR20, FPR21, FPR22, FPR23,
|
|
FPR24, FPR25, FPR26, FPR27, FPR28, FPR29, FPR30, FPR31 };
|
|
|
|
#define PC CR18H
|
|
#define NPC CR18T
|
|
#define SP GR30
|
|
|
|
struct registers {
|
|
int intregs[NUMGPRS + NUMSRS + NUMCRS + NUMSPCLS];
|
|
int fpregs [NUMFPRS * 2];
|
|
};
|
|
/* Global Variables */
|
|
|
|
static int initialized = 0; /* !0 means we've been initialized */
|
|
static unsigned char hexchars[]="0123456789abcdef";
|
|
static unsigned char remcomInBuffer[BUFMAX];
|
|
static unsigned char remcomOutBuffer[BUFMAX];
|
|
static unsigned int i_cache_params[6];
|
|
|
|
/* This table contains the mapping between PA hardware exception
|
|
types, and signals, which are primarily what GDB understands. It also
|
|
indicates which hardware traps we need to commandeer when initializing
|
|
the stub.
|
|
|
|
The only two currently used are Recovery counter (single stepping)
|
|
and Break trap ( break points ).
|
|
*/
|
|
|
|
static struct hard_trap_info
|
|
{
|
|
unsigned char tt; /* Trap number for PA-RISC */
|
|
unsigned char signo; /* Signal that we map this trap into */
|
|
} hard_trap_info[] = {
|
|
/* 1 High priority machine check */
|
|
/* 2 Power failure interrupt*/
|
|
/* 3 Recovery counter -- init */
|
|
/* 4 External interrupt */
|
|
/* 5 Low priority machine check */
|
|
{6, SIGSEGV}, /* Instruction TLB miss/page fault */
|
|
{7, SIGSEGV}, /* Memory protection */
|
|
{8, SIGILL}, /* Illegal instruction */
|
|
{9, SIGTRAP}, /* Break instruction -- init */
|
|
{10,SIGILL}, /* Privileged instruction */
|
|
{11,SIGILL}, /* Privileged register */
|
|
{12,SIGUSR1}, /* Overflow */
|
|
{13,SIGUSR2}, /* Conditional */
|
|
{14,SIGEMT}, /* Assist Exception */
|
|
{15,SIGSEGV}, /* Data TLB miss/page fault */
|
|
{16,SIGSEGV}, /* Non-access Instruction TLB miss */
|
|
{17,SIGSEGV}, /* Non-access Data TLB miss/page fault */
|
|
{18,SIGSEGV}, /* Data memory protection/ unaligned data reference */
|
|
{19,SIGTRAP}, /* Data memory break */
|
|
{20,SIGSEGV}, /* TLB dirty bit */
|
|
{21,SIGSEGV}, /* Page reference */
|
|
{22,SIGEMT}, /* Assist emulation */
|
|
{23,SIGILL}, /* Higher-privilege */
|
|
{24,SIGILL}, /* Lower-privilege */
|
|
{25,SIGTRAP}, /* Taken branch */
|
|
{0, 0} /* Must be last */
|
|
};
|
|
|
|
/* Functions */
|
|
/*========================================================================== */
|
|
|
|
/* Convert ch from a hex digit to an int */
|
|
|
|
static int
|
|
hex(ch)
|
|
unsigned char ch;
|
|
{
|
|
if (ch >= 'a' && ch <= 'f')
|
|
return ch-'a'+10;
|
|
if (ch >= '0' && ch <= '9')
|
|
return ch-'0';
|
|
if (ch >= 'A' && ch <= 'F')
|
|
return ch-'A'+10;
|
|
return -1;
|
|
}
|
|
|
|
/* scan for the sequence $<data>#<checksum> */
|
|
|
|
static void
|
|
getpacket(buffer)
|
|
char *buffer;
|
|
{
|
|
unsigned char checksum;
|
|
unsigned char xmitcsum;
|
|
int i;
|
|
int count;
|
|
unsigned char ch;
|
|
|
|
do
|
|
{
|
|
/* wait around for the start character, ignore all other characters */
|
|
strobe();
|
|
while ((ch = getDebugChar()) != '$') ;
|
|
|
|
checksum = 0;
|
|
xmitcsum = -1;
|
|
|
|
count = 0;
|
|
|
|
/* now, read until a # or end of buffer is found */
|
|
while (count < BUFMAX)
|
|
{
|
|
ch = getDebugChar();
|
|
if (ch == '#')
|
|
break;
|
|
checksum = checksum + ch;
|
|
buffer[count] = ch;
|
|
count = count + 1;
|
|
}
|
|
|
|
if (count >= BUFMAX)
|
|
continue;
|
|
|
|
buffer[count] = 0;
|
|
|
|
if (ch == '#')
|
|
{
|
|
xmitcsum = hex(getDebugChar()) << 4;
|
|
xmitcsum |= hex(getDebugChar());
|
|
|
|
#if TESTING
|
|
/* Humans shouldn't have to figure out checksums to type to it. */
|
|
putDebugChar ('+');
|
|
return;
|
|
#endif
|
|
if (checksum != xmitcsum)
|
|
putDebugChar('-'); /* failed checksum */
|
|
else
|
|
{
|
|
putDebugChar('+'); /* successful transfer */
|
|
/* if a sequence char is present, reply the sequence ID */
|
|
if (buffer[2] == ':')
|
|
{
|
|
putDebugChar(buffer[0]);
|
|
putDebugChar(buffer[1]);
|
|
/* remove sequence chars from buffer */
|
|
count = strlen(buffer);
|
|
for (i=3; i <= count; i++)
|
|
buffer[i-3] = buffer[i];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
while (checksum != xmitcsum);
|
|
}
|
|
|
|
/* send the packet in buffer. */
|
|
|
|
static void
|
|
putpacket(buffer)
|
|
unsigned char *buffer;
|
|
{
|
|
unsigned char checksum;
|
|
int count;
|
|
unsigned char ch;
|
|
|
|
/* $<packet info>#<checksum>. */
|
|
|
|
do
|
|
{
|
|
putDebugChar('$');
|
|
checksum = 0;
|
|
count = 0;
|
|
|
|
while (ch = buffer[count])
|
|
{
|
|
if (! putDebugChar(ch))
|
|
return;
|
|
checksum += ch;
|
|
count += 1;
|
|
}
|
|
|
|
putDebugChar('#');
|
|
putDebugChar(hexchars[checksum >> 4]);
|
|
putDebugChar(hexchars[checksum & 0xf]);
|
|
} while (getDebugChar() != '+');
|
|
}
|
|
|
|
/* Convert the memory pointed to by mem into hex, placing result in buf.
|
|
* Return a pointer to the last char put in buf (null), in case of mem fault,
|
|
* return 0.
|
|
* If MAY_FAULT is non-zero, then we will handle memory faults by returning
|
|
* a 0, else treat a fault like any other fault in the stub.
|
|
*/
|
|
|
|
static unsigned char *
|
|
mem2hex(mem, buf, count, may_fault)
|
|
unsigned char *mem;
|
|
unsigned char *buf;
|
|
int count;
|
|
int may_fault;
|
|
{
|
|
unsigned char ch;
|
|
int check_addr,
|
|
new_addr;
|
|
|
|
check_addr = 0;
|
|
|
|
while (count-- > 0)
|
|
{
|
|
if (may_fault)
|
|
{ new_addr = ((int) (mem+3)) & 0xFFFFFFF8;
|
|
if (new_addr != check_addr)
|
|
{ check_addr = new_addr;
|
|
if (pdc_call(OPT_PDC_ADD_VALID,0,check_addr)) return 0;
|
|
}
|
|
}
|
|
ch = *mem++;
|
|
*buf++ = hexchars[ch >> 4];
|
|
*buf++ = hexchars[ch & 0xf];
|
|
}
|
|
|
|
*buf = 0;
|
|
|
|
return buf;
|
|
}
|
|
|
|
/* convert the hex array pointed to by buf into binary to be placed in mem
|
|
* return a pointer to the character AFTER the last byte written */
|
|
|
|
static unsigned char *
|
|
hex2mem(buf, mem, count, may_fault)
|
|
unsigned char *buf;
|
|
unsigned char *mem;
|
|
int count;
|
|
int may_fault;
|
|
{
|
|
int i;
|
|
unsigned int ch;
|
|
int check_addr,
|
|
new_addr;
|
|
|
|
check_addr = 0;
|
|
|
|
for (i=0; i<count; i++)
|
|
{
|
|
ch = hex(*buf++) << 4;
|
|
ch |= hex(*buf++);
|
|
if (may_fault)
|
|
{ new_addr = ((int)(mem+3)) & 0xFFFFFFF8;
|
|
if (new_addr != check_addr)
|
|
{ check_addr = new_addr;
|
|
if (pdc_call(OPT_PDC_ADD_VALID,0,check_addr)) return 0;
|
|
}
|
|
}
|
|
*mem++ = ch;
|
|
}
|
|
|
|
return mem;
|
|
}
|
|
|
|
/* Set up exception handlers for traceing and breakpoints */
|
|
|
|
void
|
|
set_debug_traps()
|
|
{
|
|
unsigned int R_addr[33];
|
|
unsigned int *Raddr_ptr;
|
|
|
|
setup_vectors();
|
|
|
|
/* get cache params for use by flush_i_cache */
|
|
RADDR_ALIGN(Raddr_ptr,R_addr);
|
|
|
|
if (pdc_call(OPT_PDC_CACHE,0,Raddr_ptr,0))
|
|
i_cache_params[0] = -1;
|
|
else
|
|
i_cache_params[0] = R_addr[0];
|
|
|
|
i_cache_params[1] = Raddr_ptr[1];
|
|
i_cache_params[2] = Raddr_ptr[2];
|
|
i_cache_params[3] = Raddr_ptr[3];
|
|
i_cache_params[4] = Raddr_ptr[4];
|
|
i_cache_params[5] = Raddr_ptr[5];
|
|
|
|
/* In case GDB is started before us, ack any packets (presumably
|
|
"$?#xx") sitting there. */
|
|
|
|
putDebugChar ('+');
|
|
|
|
initialized = 1;
|
|
}
|
|
|
|
|
|
/* Convert the PA-RISC hardware trap number to a unix signal number. */
|
|
|
|
static int
|
|
computeSignal(tt)
|
|
int tt;
|
|
{
|
|
struct hard_trap_info *ht;
|
|
|
|
for (ht = hard_trap_info; ht->tt && ht->signo; ht++)
|
|
if (ht->tt == tt)
|
|
return ht->signo;
|
|
|
|
return SIGHUP; /* default for things we don't know about */
|
|
}
|
|
|
|
/*
|
|
* While we find nice hex chars, build an int.
|
|
* Return number of chars processed.
|
|
*/
|
|
|
|
static int
|
|
hexToInt(ptr, intValue)
|
|
unsigned char **ptr;
|
|
int *intValue;
|
|
{
|
|
int numChars = 0;
|
|
int hexValue;
|
|
|
|
*intValue = 0;
|
|
|
|
while (**ptr)
|
|
{
|
|
hexValue = hex(**ptr);
|
|
if (hexValue < 0)
|
|
break;
|
|
|
|
*intValue = (*intValue << 4) | hexValue;
|
|
numChars ++;
|
|
|
|
(*ptr)++;
|
|
}
|
|
|
|
return (numChars);
|
|
}
|
|
|
|
void
|
|
flush_i_cache()
|
|
|
|
{
|
|
unsigned int addr,count,loop;
|
|
|
|
if (i_cache_params[0] <= 0) return;
|
|
|
|
addr = i_cache_params[2];
|
|
for (count = 0; count < i_cache_params[4]; count++)
|
|
{ for ( loop = 0; loop < i_cache_params[5]; loop++) FICE(addr);
|
|
addr = addr + i_cache_params[3];
|
|
}
|
|
}
|
|
|
|
/*
|
|
* This function does all command procesing for interfacing to gdb.
|
|
return of 0 will execute DEBUG_GO (continue)
|
|
return of 1 will execute DEBUG_SS (single step)
|
|
*/
|
|
|
|
int
|
|
handle_exception (registers,tt)
|
|
unsigned long *registers;
|
|
int tt; /* Trap type */
|
|
{
|
|
int sigval;
|
|
int addr;
|
|
int length;
|
|
unsigned char *ptr;
|
|
|
|
/* reply to host that an exception has occurred */
|
|
sigval = computeSignal(tt);
|
|
ptr = remcomOutBuffer;
|
|
|
|
*ptr++ = 'T';
|
|
*ptr++ = hexchars[sigval >> 4];
|
|
*ptr++ = hexchars[sigval & 0xf];
|
|
|
|
/* could be lots of stuff here like PC and SP registers */
|
|
|
|
*ptr++ = 0;
|
|
|
|
putpacket(remcomOutBuffer);
|
|
|
|
while (1)
|
|
{
|
|
remcomOutBuffer[0] = 0;
|
|
|
|
getpacket(remcomInBuffer);
|
|
switch (remcomInBuffer[0])
|
|
{
|
|
case '?':
|
|
remcomOutBuffer[0] = 'S';
|
|
remcomOutBuffer[1] = hexchars[sigval >> 4];
|
|
remcomOutBuffer[2] = hexchars[sigval & 0xf];
|
|
remcomOutBuffer[3] = 0;
|
|
break;
|
|
|
|
case 'd':
|
|
/* toggle debug flag */
|
|
led_putnum (16);
|
|
break;
|
|
|
|
case 'g': /* return the value of the CPU registers */
|
|
{
|
|
ptr = remcomOutBuffer;
|
|
/* GR0..GR31 SR0..SR7 CR0..CR31 specials */
|
|
ptr = mem2hex((char *)registers, ptr, NUMREGBYTES, 0);
|
|
/* need to add floating point registers */
|
|
}
|
|
break;
|
|
|
|
case 'G': /* set the value of the CPU registers - return OK */
|
|
{
|
|
ptr = &remcomInBuffer[1];
|
|
/* GR0..GR31 SR0..SR7 CR0..CR31 specials */
|
|
hex2mem(ptr, (char *)registers, NUMREGBYTES, 0);
|
|
strcpy(remcomOutBuffer,"OK 1");
|
|
}
|
|
break;
|
|
|
|
case 'm': /* mAA..AA,LLLL Read LLLL bytes at address AA..AA */
|
|
/* Try to read %x,%x. */
|
|
|
|
ptr = &remcomInBuffer[1];
|
|
|
|
if (hexToInt(&ptr, &addr)
|
|
&& *ptr++ == ','
|
|
&& hexToInt(&ptr, &length))
|
|
{
|
|
if (mem2hex((char *)addr, remcomOutBuffer, length, 1))
|
|
break;
|
|
|
|
strcpy (remcomOutBuffer, "E03");
|
|
}
|
|
else
|
|
strcpy(remcomOutBuffer,"E01");
|
|
break;
|
|
|
|
case 'M': /* MAA..AA,LLLL: Write LLLL bytes at address AA.AA return OK */
|
|
/* Try to read '%x,%x:'. */
|
|
|
|
ptr = &remcomInBuffer[1];
|
|
|
|
if (hexToInt(&ptr, &addr)
|
|
&& *ptr++ == ','
|
|
&& hexToInt(&ptr, &length)
|
|
&& *ptr++ == ':')
|
|
{
|
|
if (hex2mem(ptr, (char *)addr, length, 1))
|
|
strcpy(remcomOutBuffer, "OK");
|
|
else
|
|
strcpy(remcomOutBuffer, "E03");
|
|
}
|
|
else
|
|
strcpy(remcomOutBuffer, "E02");
|
|
break;
|
|
|
|
case 'c': /* cAA..AA Continue at address AA..AA(optional) */
|
|
/* try to read optional parameter, pc unchanged if no parm */
|
|
|
|
ptr = &remcomInBuffer[1];
|
|
if (hexToInt(&ptr, &addr))
|
|
{
|
|
registers[PC] = addr;
|
|
registers[NPC] = addr + 4;
|
|
}
|
|
|
|
/* Need to flush the instruction cache here, as we may have deposited a
|
|
breakpoint, and the icache probably has no way of knowing that a data ref to
|
|
some location may have changed something that is in the instruction cache.
|
|
*/
|
|
|
|
flush_i_cache();
|
|
return 0; /* execute GO */
|
|
|
|
/* kill the program */
|
|
case 'k' : /* do nothing */
|
|
break;
|
|
|
|
case 's' : /* single step */
|
|
/* try to read optional parameter, pc unchanged if no parm */
|
|
|
|
ptr = &remcomInBuffer[1];
|
|
if (hexToInt(&ptr, &addr))
|
|
{
|
|
registers[PC] = addr;
|
|
registers[NPC] = addr + 4;
|
|
}
|
|
/* Need to flush the instruction cache here, as we may have deposited a
|
|
breakpoint, and the icache probably has no way of knowing that a data ref to
|
|
some location may have changed something that is in the instruction cache.
|
|
*/
|
|
flush_i_cache();
|
|
return 1; /* execute Single Step */
|
|
break;
|
|
|
|
#if TESTING1
|
|
case 't': /* Test feature */
|
|
break;
|
|
#endif
|
|
case 'r': /* Reset */
|
|
break;
|
|
|
|
#if TESTING2
|
|
Disabled until we can unscrew this properly
|
|
|
|
case 'b': /* bBB... Set baud rate to BB... */
|
|
{
|
|
int baudrate;
|
|
extern void set_timer_3();
|
|
|
|
ptr = &remcomInBuffer[1];
|
|
if (!hexToInt(&ptr, &baudrate))
|
|
{
|
|
strcpy(remcomOutBuffer,"B01");
|
|
break;
|
|
}
|
|
|
|
/* Convert baud rate to uart clock divider */
|
|
switch (baudrate)
|
|
{
|
|
case 38400:
|
|
baudrate = 16;
|
|
break;
|
|
case 19200:
|
|
baudrate = 33;
|
|
break;
|
|
case 9600:
|
|
baudrate = 65;
|
|
break;
|
|
default:
|
|
strcpy(remcomOutBuffer,"B02");
|
|
goto x1;
|
|
}
|
|
|
|
putpacket("OK 2"); /* Ack before changing speed */
|
|
set_timer_3(baudrate); /* Set it */
|
|
}
|
|
x1: break;
|
|
#endif
|
|
} /* switch */
|
|
|
|
/* reply to the request */
|
|
putpacket(remcomOutBuffer);
|
|
}
|
|
print ("\r\nEscaped handle_exception\r\n");
|
|
}
|