qpc/source/qs.c
Quantum Leaps cd6736f1fd 5.4.0
2015-04-28 13:45:35 -04:00

686 lines
25 KiB
C

/**
* @file
* @brief QS software tracing services
* @ingroup qs
* @cond
******************************************************************************
* Last updated for version 5.4.0
* Last updated on 2015-04-23
*
* Q u a n t u m L e a P s
* ---------------------------
* innovating embedded systems
*
* Copyright (C) Quantum Leaps, www.state-machine.com.
*
* This program is open source 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 3 of the License, or
* (at your option) any later version.
*
* Alternatively, this program may be distributed and modified under the
* terms of Quantum Leaps commercial licenses, which expressly supersede
* the GNU General Public License and are specifically designed for
* licensees interested in retaining the proprietary status of their code.
*
* 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, see <http://www.gnu.org/licenses/>.
*
* Contact information:
* Web: www.state-machine.com
* Email: info@state-machine.com
******************************************************************************
* @endcond
*/
#include "qs_port.h" /* QS port */
#include "qs_pkg.h" /* QS package-scope interface */
#include "qassert.h" /* QP embedded systems-friendly assertions */
Q_DEFINE_THIS_MODULE("qs")
/****************************************************************************/
QSPriv QS_priv_; /* QS private data */
/****************************************************************************/
/**
* @description
* This function should be called from QS_onStartup() to provide QS with
* the data buffer. The first parameter @p sto[] is the address of the memory
* block, and the second parameter @p stoSize is the size of this block
* in bytes. Currently the size of the QS buffer cannot exceed 64KB.
*
* @note QS can work with quite small data buffers, but you will start losing
* data if the buffer is too small for the bursts of tracing activity.
* The right size of the buffer depends on the data production rate and
* the data output rate. QS offers flexible filtering to reduce the data
* production rate.
*
* @note If the data output rate cannot keep up with the production rate,
* QS will start overwriting the older data with newer data. This is
* consistent with the "last-is-best" QS policy. The record sequence counters
* and check sums on each record allow the QSPY host uitiliy to easily detect
* any data loss.
*/
void QS_initBuf(uint8_t sto[], uint_fast16_t stoSize) {
uint8_t *buf = &sto[0];
/* must be at least 8 bytes */
Q_REQUIRE_ID(100, stoSize > (uint_fast16_t)8);
/* The QS_initBuf() function clears the internal QS variables, so that
* the tracing can start correctly even if the startup code fails
* to clear the uninitialized data (as is required by the C Standard).
*/
QF_bzero(&QS_priv_, (uint_fast16_t)sizeof(QS_priv_));
QS_priv_.buf = buf;
QS_priv_.end = (QSCtr)stoSize;
QS_beginRec((uint_fast8_t)QS_EMPTY);
QS_endRec();
QS_beginRec((uint_fast8_t)QS_QP_RESET);
QS_endRec();
}
/****************************************************************************/
/**
* @description
* This function sets up the QS filter to enable the record type @p rec.
* The argument @a #QS_ALL_RECORDS specifies to filter-in all records.
* This function should be called indirectly through the macro QS_FILTER_ON.
*
* @note Filtering based on the record-type is only the first layer of
* filtering. The second layer is based on the object-type. Both filter
* layers must be enabled for the QS record to be inserted into the QS buffer.
* @sa QS_filterOff(), QS_FILTER_SM_OBJ, QS_FILTER_AO_OBJ, QS_FILTER_MP_OBJ,
* QS_FILTER_EQ_OBJ, and QS_FILTER_TE_OBJ.
*/
void QS_filterOn(uint_fast8_t rec) {
if (rec == QS_ALL_RECORDS) {
uint_fast8_t i;
for (i = (uint_fast8_t)0;
i < (uint_fast8_t)(sizeof(QS_priv_.glbFilter) - 1U);
++i)
{
QS_priv_.glbFilter[i] = (uint8_t)0xFFU; /* set all bits */
}
/* never turn the last 3 records on (0x7D, 0x7E, 0x7F) */
QS_priv_.glbFilter[sizeof(QS_priv_.glbFilter) - 1U] = (uint8_t)0x1F;
}
else {
/* record numbers can't exceed QS_ESC, so they don't need escaping */
Q_ASSERT_ID(210, rec < (uint_fast8_t)QS_ESC);
QS_priv_.glbFilter[rec >> 3] |=
(uint8_t)(1U << (rec & (uint_fast8_t)7));
}
}
/****************************************************************************/
/**
* @description
* This function sets up the QS filter to disable the record type @p rec.
* The argument @a #QS_ALL_RECORDS specifies to suppress all records.
* This function should be called indirectly through the macro QS_FILTER_OFF.
*
* @note Filtering records based on the record-type is only the first layer of
* filtering. The second layer is based on the object-type. Both filter
* layers must be enabled for the QS record to be inserted into the QS buffer.
*/
void QS_filterOff(uint_fast8_t rec) {
if (rec == QS_ALL_RECORDS) {
uint8_t *glbFilter = &QS_priv_.glbFilter[0];
/* the following unrolled loop is designed to stop collecting trace
* very fast in order to prevent overwriting the interesting data.
* The code assumes that the dimension of QS_priv_.glbFilter[] is 16.
*/
*glbFilter = (uint8_t)0; QS_PTR_INC_(glbFilter);
*glbFilter = (uint8_t)0; QS_PTR_INC_(glbFilter);
*glbFilter = (uint8_t)0; QS_PTR_INC_(glbFilter);
*glbFilter = (uint8_t)0; QS_PTR_INC_(glbFilter);
*glbFilter = (uint8_t)0; QS_PTR_INC_(glbFilter);
*glbFilter = (uint8_t)0; QS_PTR_INC_(glbFilter);
*glbFilter = (uint8_t)0; QS_PTR_INC_(glbFilter);
*glbFilter = (uint8_t)0; QS_PTR_INC_(glbFilter);
*glbFilter = (uint8_t)0; QS_PTR_INC_(glbFilter);
*glbFilter = (uint8_t)0; QS_PTR_INC_(glbFilter);
*glbFilter = (uint8_t)0; QS_PTR_INC_(glbFilter);
*glbFilter = (uint8_t)0; QS_PTR_INC_(glbFilter);
*glbFilter = (uint8_t)0; QS_PTR_INC_(glbFilter);
*glbFilter = (uint8_t)0; QS_PTR_INC_(glbFilter);
*glbFilter = (uint8_t)0; QS_PTR_INC_(glbFilter);
*glbFilter = (uint8_t)0;
}
else {
uint8_t tmp;
/* record numbers can't exceed QS_ESC, so they don't need escaping */
Q_ASSERT_ID(310, rec < (uint_fast8_t)QS_ESC);
tmp = (uint8_t)(1U << (rec & (uint_fast8_t)0x07));
tmp ^= (uint8_t)0xFFU; /* invert all bits */
QS_priv_.glbFilter[rec >> 3] &= tmp;
}
}
/****************************************************************************/
/**
* @description
* This function must be called at the beginning of each QS record.
* This function should be called indirectly through the macro #QS_BEGIN,
* or #QS_BEGIN_NOCRIT, depending if it's called in a normal code or from
* a critical section.
*/
void QS_beginRec(uint_fast8_t rec) {
uint8_t b = (uint8_t)(QS_priv_.seq + (uint8_t)1);
uint8_t chksum = (uint8_t)0; /* reset the checksum */
uint8_t *buf = QS_priv_.buf; /* put in a temporary (register) */
QSCtr head = QS_priv_.head; /* put in a temporary (register) */
QSCtr end = QS_priv_.end; /* put in a temporary (register) */
QS_priv_.seq = b; /* store the incremented sequence num */
QS_priv_.used += (QSCtr)2; /* 2 bytes about to be added */
QS_INSERT_ESC_BYTE(b)
chksum = (uint8_t)(chksum + (uint8_t)rec); /* update checksum */
QS_INSERT_BYTE((uint8_t)rec) /* rec byte does not need escaping */
QS_priv_.head = head; /* save the head */
QS_priv_.chksum = chksum; /* save the checksum */
}
/****************************************************************************/
/**
* @description
* This function must be called at the end of each QS record.
* This function should be called indirectly through the macro #QS_END,
* or #QS_END_NOCRIT, depending if it's called in a normal code or from
* a critical section.
*/
void QS_endRec(void) {
uint8_t *buf = QS_priv_.buf; /* put in a temporary (register) */
QSCtr head = QS_priv_.head;
QSCtr end = QS_priv_.end;
uint8_t b = QS_priv_.chksum;
b ^= (uint8_t)0xFFU; /* invert the bits in the checksum */
QS_priv_.used += (QSCtr)2; /* 2 bytes about to be added */
if ((b != QS_FRAME) && (b != QS_ESC)) {
QS_INSERT_BYTE(b)
}
else {
QS_INSERT_BYTE(QS_ESC)
QS_INSERT_BYTE(b ^ QS_ESC_XOR)
++QS_priv_.used; /* account for the ESC byte */
}
QS_INSERT_BYTE(QS_FRAME) /* do not escape this QS_FRAME */
QS_priv_.head = head; /* save the head */
/* overrun over the old data? */
if (QS_priv_.used > end) {
QS_priv_.used = end; /* the whole buffer is used */
QS_priv_.tail = head; /* shift the tail to the old data */
}
}
/****************************************************************************/
/**
* @description
* @note This function is only to be used through macros, never in the
* client code directly.
*/
void QS_u8(uint8_t format, uint8_t d) {
uint8_t chksum = QS_priv_.chksum; /* put in a temporary (register) */
uint8_t *buf = QS_priv_.buf; /* put in a temporary (register) */
QSCtr head = QS_priv_.head; /* put in a temporary (register) */
QSCtr end = QS_priv_.end; /* put in a temporary (register) */
QS_priv_.used += (QSCtr)2; /* 2 bytes about to be added */
QS_INSERT_ESC_BYTE(format)
QS_INSERT_ESC_BYTE(d)
QS_priv_.head = head; /* save the head */
QS_priv_.chksum = chksum; /* save the checksum */
}
/****************************************************************************/
/**
* @description
* This function is only to be used through macros, never in the
* client code directly.
*/
void QS_u16(uint8_t format, uint16_t d) {
uint8_t chksum = QS_priv_.chksum; /* put in a temporary (register) */
uint8_t *buf = QS_priv_.buf; /* put in a temporary (register) */
QSCtr head = QS_priv_.head; /* put in a temporary (register) */
QSCtr end = QS_priv_.end; /* put in a temporary (register) */
QS_priv_.used += (QSCtr)3; /* 3 bytes about to be added */
QS_INSERT_ESC_BYTE(format)
format = (uint8_t)d;
QS_INSERT_ESC_BYTE(format)
d >>= 8;
format = (uint8_t)d;
QS_INSERT_ESC_BYTE(format)
QS_priv_.head = head; /* save the head */
QS_priv_.chksum = chksum; /* save the checksum */
}
/****************************************************************************/
/**
* @note This function is only to be used through macros, never in the
* client code directly.
*/
void QS_u32(uint8_t format, uint32_t d) {
uint8_t chksum = QS_priv_.chksum; /* put in a temporary (register) */
uint8_t *buf = QS_priv_.buf; /* put in a temporary (register) */
QSCtr head = QS_priv_.head; /* put in a temporary (register) */
QSCtr end = QS_priv_.end; /* put in a temporary (register) */
int_fast8_t i;
QS_priv_.used += (QSCtr)5; /* 5 bytes about to be added */
QS_INSERT_ESC_BYTE(format) /* insert the format byte */
/* insert 4 bytes... */
for (i = (int_fast8_t)4; i != (int_fast8_t)0; --i) {
format = (uint8_t)d;
QS_INSERT_ESC_BYTE(format)
d >>= 8;
}
QS_priv_.head = head; /* save the head */
QS_priv_.chksum = chksum; /* save the checksum */
}
/****************************************************************************/
/*! output uint8_t data element without format information */
/** @note This function is only to be used through macros, never in the
* client code directly.
*/
void QS_u8_(uint8_t d) {
uint8_t chksum = QS_priv_.chksum; /* put in a temporary (register) */
uint8_t *buf = QS_priv_.buf; /* put in a temporary (register) */
QSCtr head = QS_priv_.head; /* put in a temporary (register) */
QSCtr end = QS_priv_.end; /* put in a temporary (register) */
++QS_priv_.used; /* 1 byte about to be added */
QS_INSERT_ESC_BYTE(d)
QS_priv_.head = head; /* save the head */
QS_priv_.chksum = chksum; /* save the checksum */
}
/****************************************************************************/
/** @note This function is only to be used through macros, never in the
* client code directly.
*/
void QS_u8u8_(uint8_t d1, uint8_t d2) {
uint8_t chksum = QS_priv_.chksum; /* put in a temporary (register) */
uint8_t *buf = QS_priv_.buf; /* put in a temporary (register) */
QSCtr head = QS_priv_.head; /* put in a temporary (register) */
QSCtr end = QS_priv_.end; /* put in a temporary (register) */
QS_priv_.used += (QSCtr)2; /* 2 bytes are about to be added */
QS_INSERT_ESC_BYTE(d1)
QS_INSERT_ESC_BYTE(d2)
QS_priv_.head = head; /* save the head */
QS_priv_.chksum = chksum; /* save the checksum */
}
/****************************************************************************/
/**
* @note This function is only to be used through macros, never in the
* client code directly.
*/
void QS_u16_(uint16_t d) {
uint8_t b = (uint8_t)d;
uint8_t chksum = QS_priv_.chksum; /* put in a temporary (register) */
uint8_t *buf = QS_priv_.buf; /* put in a temporary (register) */
QSCtr head = QS_priv_.head; /* put in a temporary (register) */
QSCtr end = QS_priv_.end; /* put in a temporary (register) */
QS_priv_.used += (QSCtr)2; /* 2 bytes are about to be added */
QS_INSERT_ESC_BYTE(b)
d >>= 8;
b = (uint8_t)d;
QS_INSERT_ESC_BYTE(b)
QS_priv_.head = head; /* save the head */
QS_priv_.chksum = chksum; /* save the checksum */
}
/****************************************************************************/
/** @note This function is only to be used through macros, never in the
* client code directly.
*/
void QS_u32_(uint32_t d) {
uint8_t chksum = QS_priv_.chksum; /* put in a temporary (register) */
uint8_t *buf = QS_priv_.buf; /* put in a temporary (register) */
QSCtr head = QS_priv_.head; /* put in a temporary (register) */
QSCtr end = QS_priv_.end; /* put in a temporary (register) */
int_fast8_t i;
QS_priv_.used += (QSCtr)4; /* 4 bytes are about to be added */
for (i = (int_fast8_t)4; i != (int_fast8_t)0; --i) {
uint8_t b = (uint8_t)d;
QS_INSERT_ESC_BYTE(b)
d >>= 8;
}
QS_priv_.head = head; /* save the head */
QS_priv_.chksum = chksum; /* save the checksum */
}
/****************************************************************************/
/**
* @note This function is only to be used through macros, never in the
* client code directly.
*/
void QS_str_(char_t const *s) {
uint8_t b = (uint8_t)(*s);
uint8_t chksum = QS_priv_.chksum; /* put in a temporary (register) */
uint8_t *buf = QS_priv_.buf; /* put in a temporary (register) */
QSCtr head = QS_priv_.head; /* put in a temporary (register) */
QSCtr end = QS_priv_.end; /* put in a temporary (register) */
QSCtr used = QS_priv_.used; /* put in a temporary (register) */
while (b != (uint8_t)(0)) {
chksum = (uint8_t)(chksum + b); /* update checksum */
QS_INSERT_BYTE(b) /* ASCII characters don't need escaping */
QS_PTR_INC_(s);
b = (uint8_t)(*s);
++used;
}
QS_INSERT_BYTE((uint8_t)0) /* zero-terminate the string */
++used;
QS_priv_.head = head; /* save the head */
QS_priv_.chksum = chksum; /* save the checksum */
QS_priv_.used = used; /* save # of used buffer space */
}
/****************************************************************************/
/**
* @note This function is only to be used through macros, never in the
* client code directly.
*/
void QS_str_ROM_(char_t const Q_ROM *s) {
uint8_t b = (uint8_t)Q_ROM_BYTE(*s);
uint8_t chksum = QS_priv_.chksum; /* put in a temporary (register) */
uint8_t *buf = QS_priv_.buf; /* put in a temporary (register) */
QSCtr head = QS_priv_.head; /* put in a temporary (register) */
QSCtr end = QS_priv_.end; /* put in a temporary (register) */
QSCtr used = QS_priv_.used; /* put in a temporary (register) */
while (b != (uint8_t)(0)) {
chksum = (uint8_t)(chksum + b); /* update checksum */
QS_INSERT_BYTE(b) /* ASCII characters don't need escaping */
QS_PTR_INC_(s);
b = (uint8_t)Q_ROM_BYTE(*s);
++used;
}
QS_INSERT_BYTE((uint8_t)0) /* zero-terminate the string */
++used;
QS_priv_.head = head; /* save the head */
QS_priv_.chksum = chksum; /* save the checksum */
QS_priv_.used = used; /* save # of used buffer space */
}
/****************************************************************************/
/**
* @description
* This function delivers one byte at a time from the QS data buffer.
*
* @returns the byte in the least-significant 8-bits of the 16-bit return
* value if the byte is available. If no more data is available at the time,
* the function returns ::QS_EOD (End-Of-Data).
*
* @note QS_getByte() is __not__ protected with a critical section.
*/
uint16_t QS_getByte(void) {
uint16_t ret;
if (QS_priv_.used == (QSCtr)0) {
ret = QS_EOD; /* set End-Of-Data */
}
else {
uint8_t *buf = QS_priv_.buf; /* put in a temporary (register) */
QSCtr tail = QS_priv_.tail; /* put in a temporary (register) */
ret = (uint16_t)(*QS_PTR_AT_(tail)); /* set the byte to return */
++tail; /* advance the tail */
if (tail == QS_priv_.end) { /* tail wrap around? */
tail = (QSCtr)0;
}
QS_priv_.tail = tail; /* update the tail */
--QS_priv_.used; /* one less byte used */
}
return ret; /* return the byte or EOD */
}
/****************************************************************************/
/**
* @description
* This function delivers a contiguous block of data from the QS data buffer.
* The function returns the pointer to the beginning of the block, and writes
* the number of bytes in the block to the location pointed to by @p pNbytes.
* The parameter @p pNbytes is also used as input to provide the maximum size
* of the data block that the caller can accept.
*
* @returns if data is available, the function returns pointer to the
* contiguous block of data and sets the value pointed to by @p pNbytes
* to the # available bytes. If data is available at the time the function is
* called, the function returns NULL pointer and sets the value pointed to by
* @p pNbytes to zero.
*
* @note Only the NULL return from QS_getBlock() indicates that the QS buffer
* is empty at the time of the call. The non-NULL return often means that
* the block is at the end of the buffer and you need to call QS_getBlock()
* again to obtain the rest of the data that "wrapped around" to the
* beginning of the QS data buffer.
*
* @note QS_getBlock() is NOT protected with a critical section.
*/
uint8_t const *QS_getBlock(uint16_t *pNbytes) {
QSCtr used = QS_priv_.used; /* put in a temporary (register) */
uint8_t *buf;
/* any bytes used in the ring buffer? */
if (used != (QSCtr)0) {
QSCtr tail = QS_priv_.tail; /* put in a temporary (register) */
QSCtr end = QS_priv_.end; /* put in a temporary (register) */
QSCtr n = (QSCtr)(end - tail);
if (n > used) {
n = used;
}
if (n > (QSCtr)(*pNbytes)) {
n = (QSCtr)(*pNbytes);
}
*pNbytes = (uint16_t)n; /* n-bytes available */
buf = QS_priv_.buf;
buf = QS_PTR_AT_(tail); /* the bytes are at the tail */
QS_priv_.used = (QSCtr)(used - n);
tail += n;
if (tail == end) {
tail = (QSCtr)0;
}
QS_priv_.tail = tail;
}
else { /* no bytes available */
*pNbytes = (uint16_t)0; /* no bytes available right now */
buf = (uint8_t *)0; /* no bytes available right now */
}
return buf;
}
/****************************************************************************/
/** @note This function is only to be used through macro QS_SIG_DICTIONARY()
*/
void QS_sig_dict(enum_t const sig, void const * const obj,
char_t const Q_ROM * const name)
{
QS_CRIT_STAT_
QS_CRIT_ENTRY_();
QS_beginRec((uint_fast8_t)QS_SIG_DICT);
QS_SIG_((QSignal)sig);
QS_OBJ_(obj);
QS_STR_ROM_(name);
QS_endRec();
QS_CRIT_EXIT_();
QS_onFlush();
}
/****************************************************************************/
/** @note This function is only to be used through macro QS_OBJ_DICTIONARY()
*/
void QS_obj_dict(void const * const obj,
char_t const Q_ROM * const name)
{
QS_CRIT_STAT_
QS_CRIT_ENTRY_();
QS_beginRec((uint_fast8_t)QS_OBJ_DICT);
QS_OBJ_(obj);
QS_STR_ROM_(name);
QS_endRec();
QS_CRIT_EXIT_();
QS_onFlush();
}
/****************************************************************************/
/** @note This function is only to be used through macro QS_FUN_DICTIONARY()
*/
void QS_fun_dict(void (* const fun)(void),
char_t const Q_ROM * const name)
{
QS_CRIT_STAT_
QS_CRIT_ENTRY_();
QS_beginRec((uint_fast8_t)QS_FUN_DICT);
QS_FUN_(fun);
QS_STR_ROM_(name);
QS_endRec();
QS_CRIT_EXIT_();
QS_onFlush();
}
/****************************************************************************/
/** @note This function is only to be used through macro QS_USR_DICTIONARY()
*/
void QS_usr_dict(enum_t const rec,
char_t const Q_ROM * const name)
{
QS_CRIT_STAT_
QS_CRIT_ENTRY_();
QS_beginRec((uint_fast8_t)QS_USR_DICT);
QS_U8_((uint8_t)rec);
QS_STR_ROM_(name);
QS_endRec();
QS_CRIT_EXIT_();
QS_onFlush();
}
/****************************************************************************/
/** @note This function is only to be used through macros, never in the
* client code directly.
*/
void QS_mem(uint8_t const *blk, uint8_t size) {
uint8_t b = (uint8_t)(QS_MEM_T);
uint8_t chksum = (uint8_t)(QS_priv_.chksum + b);
uint8_t *buf = QS_priv_.buf; /* put in a temporary (register) */
QSCtr head = QS_priv_.head; /* put in a temporary (register) */
QSCtr end = QS_priv_.end; /* put in a temporary (register) */
QS_priv_.used += ((QSCtr)size + (QSCtr)2); /* size+2 bytes to be added */
QS_INSERT_BYTE(b)
QS_INSERT_ESC_BYTE(size)
/* output the 'size' number of bytes */
while (size != (uint8_t)0) {
b = *blk;
QS_INSERT_ESC_BYTE(b)
QS_PTR_INC_(blk);
--size;
}
QS_priv_.head = head; /* save the head */
QS_priv_.chksum = chksum; /* save the checksum */
}
/****************************************************************************/
/**
* @note This function is only to be used through macros, never in the
* client code directly.
*/
void QS_str(char_t const *s) {
uint8_t b = (uint8_t)(*s);
uint8_t chksum = (uint8_t)(QS_priv_.chksum + (uint8_t)QS_STR_T);
uint8_t *buf = QS_priv_.buf; /* put in a temporary (register) */
QSCtr head = QS_priv_.head; /* put in a temporary (register) */
QSCtr end = QS_priv_.end; /* put in a temporary (register) */
QSCtr used = QS_priv_.used; /* put in a temporary (register) */
used += (QSCtr)2; /* account for the format byte and the terminating-0 */
QS_INSERT_BYTE((uint8_t)QS_STR_T)
while (b != (uint8_t)(0)) {
/* ASCII characters don't need escaping */
chksum = (uint8_t)(chksum + b); /* update checksum */
QS_INSERT_BYTE(b)
QS_PTR_INC_(s);
b = (uint8_t)(*s);
++used;
}
QS_INSERT_BYTE((uint8_t)0) /* zero-terminate the string */
QS_priv_.head = head; /* save the head */
QS_priv_.chksum = chksum; /* save the checksum */
QS_priv_.used = used; /* save # of used buffer space */
}
/****************************************************************************/
/** @note This function is only to be used through macros, never in the
* client code directly.
*/
void QS_str_ROM(char_t const Q_ROM *s) {
uint8_t b = (uint8_t)Q_ROM_BYTE(*s);
uint8_t chksum = (uint8_t)(QS_priv_.chksum + (uint8_t)QS_STR_T);
uint8_t *buf = QS_priv_.buf; /* put in a temporary (register) */
QSCtr head = QS_priv_.head; /* put in a temporary (register) */
QSCtr end = QS_priv_.end; /* put in a temporary (register) */
QSCtr used = QS_priv_.used; /* put in a temporary (register) */
used += (QSCtr)2; /* account for the format byte and the terminating-0 */
QS_INSERT_BYTE((uint8_t)QS_STR_T)
while (b != (uint8_t)(0)) {
/* ASCII characters don't need escaping */
chksum = (uint8_t)(chksum + b); /* update checksum */
QS_INSERT_BYTE(b)
QS_PTR_INC_(s);
b = (uint8_t)Q_ROM_BYTE(*s);
++used;
}
QS_INSERT_BYTE((uint8_t)0) /* zero-terminate the string */
QS_priv_.head = head; /* save the head */
QS_priv_.chksum = chksum; /* save the checksum */
QS_priv_.used = used; /* save # of used buffer space */
}