48struct QS::QSrxPriv QS::rxPriv_;
51#if (QS_OBJ_PTR_SIZE == 1U)
52 using QSObj = std::uint8_t;
53#elif (QS_OBJ_PTR_SIZE == 2U)
54 using QSObj = std::uint16_t;
55#elif (QS_OBJ_PTR_SIZE == 4U)
56 using QSObj = std::uint32_t;
57#elif (QS_OBJ_PTR_SIZE == 8U)
58 using QSObj = std::uint64_t;
73 std::uint_fast8_t rate;
93 std::uint8_t data[16];
115 #if (QS_FUN_PTR_SIZE == 1U)
116 using QSFun = std::uint8_t;
117 #elif (QS_FUN_PTR_SIZE == 2U)
118 using QSFun = std::uint16_t;
119 #elif (QS_FUN_PTR_SIZE == 4U)
120 using QSFun = std::uint32_t;
121 #elif (QS_FUN_PTR_SIZE == 8U)
122 using QSFun = std::uint64_t;
131 static struct TestData {
140static struct ExtState {
159enum RxStateEnum : std::uint8_t {
198 WAIT4_TEST_SETUP_FRAME,
199 WAIT4_TEST_TEARDOWN_FRAME,
200 WAIT4_TEST_PROBE_DATA,
201 WAIT4_TEST_PROBE_ADDR,
202 WAIT4_TEST_PROBE_FRAME,
203 WAIT4_TEST_CONTINUE_FRAME,
213static void rxPoke_(
void)
noexcept;
216static inline void tran_(RxStateEnum
const target)
noexcept {
217 l_rx.state =
static_cast<std::uint8_t
>(target);
242 std::uint16_t
const stoSize)
noexcept
244 rxPriv_.buf = &sto[0];
245 rxPriv_.end =
static_cast<QSCtr>(stoSize);
261 beginRec_(
static_cast<std::uint_fast8_t
>(
QS_OBJ_DICT));
268 l_testData.tpNum = 0U;
269 l_testData.testTime = 0U;
282 QSCtr const head = rxPriv_.head;
283 if (head == rxPriv_.tail) {
284 return static_cast<std::uint16_t
>(rxPriv_.end - 1U);
286 else if (head < rxPriv_.tail) {
287 return static_cast<std::uint16_t
>(rxPriv_.tail - head - 1U);
290 return static_cast<std::uint16_t
>(rxPriv_.end + rxPriv_.tail
302 rxPriv_.currObj[obj_kind] = obj_ptr;
325 reinterpret_cast<QHsm *
>(
326 QS::rxPriv_.currObj[obj_kind])->getStateHandler());
347 QS_SIG_PRE_(
reinterpret_cast<QTimeEvt *
>(
377 if (l_rx.esc != 0U) {
412 switch (l_rx.state) {
425 tran_(WAIT4_INFO_FRAME);
431 tran_(WAIT4_RESET_FRAME);
434 tran_(WAIT4_TICK_RATE);
438 l_rx.var.peek.offs = 0U;
439 l_rx.var.peek.idx = 0U;
440 tran_(WAIT4_PEEK_OFFS);
454 l_rx.var.poke.offs = 0U;
455 l_rx.var.poke.idx = 0U;
456 tran_(WAIT4_POKE_OFFS);
460 (l_rx.var.poke.fill != 0U)
468 l_rx.var.flt.recId = b;
469 tran_(WAIT4_FILTER_LEN);
473 l_rx.var.obj.recId = b;
474 tran_(WAIT4_OBJ_KIND);
479 tran_(WAIT4_QUERY_KIND);
482 tran_(WAIT4_EVT_PRIO);
487 tran_(WAIT4_TEST_SETUP_FRAME);
490 tran_(WAIT4_TEST_TEARDOWN_FRAME);
493 tran_(WAIT4_TEST_CONTINUE_FRAME);
497 <
static_cast<std::uint8_t
>(
498 (
sizeof(l_testData.tpBuf)
499 /
sizeof(l_testData.tpBuf[0]))))
501 l_rx.var.tp.data = 0U;
502 l_rx.var.tp.idx = 0U;
503 tran_(WAIT4_TEST_PROBE_DATA);
520 case WAIT4_INFO_FRAME: {
525 l_rx.var.cmd.cmdId = b;
526 l_rx.var.cmd.idx = 0U;
527 l_rx.var.cmd.param1 = 0U;
528 l_rx.var.cmd.param2 = 0U;
529 l_rx.var.cmd.param3 = 0U;
530 tran_(WAIT4_CMD_PARAM1);
533 case WAIT4_CMD_PARAM1: {
534 l_rx.var.cmd.param1 |=
535 (
static_cast<std::uint32_t
>(b) << l_rx.var.cmd.idx);
536 l_rx.var.cmd.idx += 8U;
537 if (l_rx.var.cmd.idx == (8U*4U)) {
538 l_rx.var.cmd.idx = 0U;
539 tran_(WAIT4_CMD_PARAM2);
543 case WAIT4_CMD_PARAM2: {
544 l_rx.var.cmd.param2 |=
545 static_cast<std::uint32_t
>(b) << l_rx.var.cmd.idx;
546 l_rx.var.cmd.idx += 8U;
547 if (l_rx.var.cmd.idx == (8U*4U)) {
548 l_rx.var.cmd.idx = 0U;
549 tran_(WAIT4_CMD_PARAM3);
553 case WAIT4_CMD_PARAM3: {
554 l_rx.var.cmd.param3 |=
555 static_cast<std::uint32_t
>(b) << l_rx.var.cmd.idx;
556 l_rx.var.cmd.idx += 8U;
557 if (l_rx.var.cmd.idx == (8U*4U)) {
558 l_rx.var.cmd.idx = 0U;
559 tran_(WAIT4_CMD_FRAME);
563 case WAIT4_CMD_FRAME: {
567 case WAIT4_RESET_FRAME: {
571 case WAIT4_TICK_RATE: {
572 l_rx.var.tick.rate =
static_cast<std::uint_fast8_t
>(b);
573 tran_(WAIT4_TICK_FRAME);
576 case WAIT4_TICK_FRAME: {
580 case WAIT4_PEEK_OFFS: {
581 if (l_rx.var.peek.idx == 0U) {
582 l_rx.var.peek.offs =
static_cast<std::uint16_t
>(b);
583 l_rx.var.peek.idx += 8U;
586 l_rx.var.peek.offs |=
static_cast<std::uint16_t
>(
587 static_cast<std::uint16_t
>(b) << 8U);
588 tran_(WAIT4_PEEK_SIZE);
592 case WAIT4_PEEK_SIZE: {
593 if ((b == 1U) || (b == 2U) || (b == 4U)) {
594 l_rx.var.peek.size = b;
595 tran_(WAIT4_PEEK_NUM);
603 case WAIT4_PEEK_NUM: {
604 l_rx.var.peek.num = b;
605 tran_(WAIT4_PEEK_FRAME);
608 case WAIT4_PEEK_FRAME: {
612 case WAIT4_POKE_OFFS: {
613 if (l_rx.var.poke.idx == 0U) {
614 l_rx.var.poke.offs =
static_cast<std::uint16_t
>(b);
615 l_rx.var.poke.idx = 1U;
618 l_rx.var.poke.offs |=
static_cast<std::uint16_t
>(
619 static_cast<std::uint16_t
>(b) << 8U);
620 tran_(WAIT4_POKE_SIZE);
624 case WAIT4_POKE_SIZE: {
629 l_rx.var.poke.size = b;
630 tran_(WAIT4_POKE_NUM);
640 case WAIT4_POKE_NUM: {
642 l_rx.var.poke.num = b;
643 l_rx.var.poke.data = 0U;
644 l_rx.var.poke.idx = 0U;
645 tran_((l_rx.var.poke.fill != 0U)
657 case WAIT4_FILL_DATA: {
658 l_rx.var.poke.data |=
659 static_cast<std::uint32_t
>(b) << l_rx.var.poke.idx;
660 l_rx.var.poke.idx += 8U;
661 if ((l_rx.var.poke.idx >> 3U) == l_rx.var.poke.size) {
662 tran_(WAIT4_FILL_FRAME);
666 case WAIT4_POKE_DATA: {
667 l_rx.var.poke.data |=
668 static_cast<std::uint32_t
>(b) << l_rx.var.poke.idx;
669 l_rx.var.poke.idx += 8U;
670 if ((l_rx.var.poke.idx >> 3U) == l_rx.var.poke.size) {
673 if (l_rx.var.poke.num == 0U) {
674 tran_(WAIT4_POKE_FRAME);
679 case WAIT4_FILL_FRAME: {
683 case WAIT4_POKE_FRAME: {
687 case WAIT4_FILTER_LEN: {
688 if (b ==
static_cast<std::uint8_t
>(
sizeof(l_rx.var.flt.data))) {
689 l_rx.var.flt.idx = 0U;
690 tran_(WAIT4_FILTER_DATA);
698 case WAIT4_FILTER_DATA: {
699 l_rx.var.flt.data[l_rx.var.flt.idx] = b;
701 if (l_rx.var.flt.idx ==
sizeof(l_rx.var.flt.data)) {
702 tran_(WAIT4_FILTER_FRAME);
706 case WAIT4_FILTER_FRAME: {
710 case WAIT4_OBJ_KIND: {
712 l_rx.var.obj.kind = b;
713 l_rx.var.obj.addr = 0U;
714 l_rx.var.obj.idx = 0U;
715 tran_(WAIT4_OBJ_ADDR);
723 case WAIT4_OBJ_ADDR: {
725 static_cast<QSObj
>(b) << l_rx.var.obj.idx;
726 l_rx.var.obj.idx += 8U;
730 tran_(WAIT4_OBJ_FRAME);
734 case WAIT4_OBJ_FRAME: {
738 case WAIT4_QUERY_KIND: {
740 l_rx.var.obj.kind = b;
741 tran_(WAIT4_QUERY_FRAME);
749 case WAIT4_QUERY_FRAME: {
753 case WAIT4_EVT_PRIO: {
754 l_rx.var.evt.prio = b;
755 l_rx.var.evt.sig = 0U;
756 l_rx.var.evt.idx = 0U;
757 tran_(WAIT4_EVT_SIG);
760 case WAIT4_EVT_SIG: {
761 l_rx.var.evt.sig |=
static_cast<QSignal>(
762 static_cast<std::uint32_t
>(b) << l_rx.var.evt.idx);
763 l_rx.var.evt.idx += 8U;
767 l_rx.var.evt.len = 0U;
768 l_rx.var.evt.idx = 0U;
769 tran_(WAIT4_EVT_LEN);
773 case WAIT4_EVT_LEN: {
774 l_rx.var.evt.len |=
static_cast<std::uint16_t
>(
775 static_cast<unsigned>(b) << l_rx.var.evt.idx);
776 l_rx.var.evt.idx += 8U;
777 if (l_rx.var.evt.idx == (8U * 2U)) {
778 if ((l_rx.var.evt.len +
sizeof(
QEvt))
785 (
static_cast<std::uint_fast16_t
>(l_rx.var.evt.len)
788 static_cast<enum_t>(l_rx.var.evt.sig));
790 if (l_rx.var.evt.e !=
nullptr) {
792 reinterpret_cast<std::uint8_t *
>(l_rx.var.evt.e);
793 l_rx.var.evt.p = &l_rx.var.evt.p[
sizeof(
QEvt)];
794 if (l_rx.var.evt.len > 0U) {
795 tran_(WAIT4_EVT_PAR);
798 tran_(WAIT4_EVT_FRAME);
813 case WAIT4_EVT_PAR: {
817 if (l_rx.var.evt.len == 0U) {
818 tran_(WAIT4_EVT_FRAME);
822 case WAIT4_EVT_FRAME: {
828 case WAIT4_TEST_SETUP_FRAME: {
832 case WAIT4_TEST_TEARDOWN_FRAME: {
836 case WAIT4_TEST_CONTINUE_FRAME: {
840 case WAIT4_TEST_PROBE_DATA: {
842 (
static_cast<QSFun
>(b) << l_rx.var.tp.idx);
843 l_rx.var.tp.idx += 8U;
844 if (l_rx.var.tp.idx == (8U *
sizeof(std::uint32_t))) {
845 l_rx.var.tp.addr = 0U;
846 l_rx.var.tp.idx = 0U;
847 tran_(WAIT4_TEST_PROBE_ADDR);
851 case WAIT4_TEST_PROBE_ADDR: {
853 (
static_cast<std::uint32_t
>(b) << l_rx.var.tp.idx);
854 l_rx.var.tp.idx += 8U;
858 tran_(WAIT4_TEST_PROBE_FRAME);
862 case WAIT4_TEST_PROBE_FRAME: {
887 case WAIT4_INFO_FRAME: {
894 case WAIT4_RESET_FRAME: {
899 case WAIT4_CMD_PARAM1:
900 case WAIT4_CMD_PARAM2:
901 case WAIT4_CMD_PARAM3:
902 case WAIT4_CMD_FRAME: {
905 l_rx.var.cmd.param2, l_rx.var.cmd.param3);
912 case WAIT4_TICK_FRAME: {
918 QF::tickX_(
static_cast<std::uint_fast8_t
>(l_rx.var.tick.rate),
924 case WAIT4_PEEK_FRAME: {
928 ptr =
static_cast<std::uint8_t*
>(
930 ptr = &ptr[l_rx.var.peek.offs];
935 for (i = 0U; i < l_rx.var.peek.num; ++i) {
936 switch (l_rx.var.peek.size) {
942 reinterpret_cast<std::uint16_t*
>(ptr)[i]);
946 reinterpret_cast<std::uint32_t*
>(ptr)[i]);
958 case WAIT4_POKE_DATA: {
963 case WAIT4_POKE_FRAME: {
968 case WAIT4_FILL_FRAME: {
970 ptr =
static_cast<std::uint8_t *
>(
972 ptr = &ptr[l_rx.var.poke.offs];
973 for (i = 0U; i < l_rx.var.poke.num; ++i) {
974 switch (l_rx.var.poke.size) {
977 static_cast<std::uint8_t
>(l_rx.var.poke.data);
980 reinterpret_cast<std::uint16_t *
>(ptr)[i] =
981 static_cast<std::uint16_t
>(l_rx.var.poke.data);
984 reinterpret_cast<std::uint32_t *
>(ptr)[i] =
993 case WAIT4_FILTER_FRAME: {
997 if (l_rx.var.flt.recId
1016 else if (l_rx.var.flt.recId
1032 case WAIT4_OBJ_FRAME: {
1033 i = l_rx.var.obj.kind;
1035 if (l_rx.var.obj.recId
1039 reinterpret_cast<void *
>(l_rx.var.obj.addr);
1042 else if (l_rx.var.obj.recId
1045 if (l_rx.var.obj.addr != 0U) {
1046 std::int_fast16_t
const filter =
1047 static_cast<std::int_fast16_t
>(
1049 l_rx.var.obj.addr)->m_prio);
1065 if (l_rx.var.obj.recId
1069 =
reinterpret_cast<void *
>(l_rx.var.obj.addr);
1071 =
reinterpret_cast<void *
>(l_rx.var.obj.addr);
1081 case WAIT4_QUERY_FRAME: {
1085 case WAIT4_EVT_FRAME: {
1093 if (l_rx.var.evt.prio == 0U) {
1106 else if (l_rx.var.evt.prio == 255U) {
1116 ->dispatch(l_rx.var.evt.e, 0U);
1123 else if (l_rx.var.evt.prio == 254U) {
1133 ->init(l_rx.var.evt.e, 0U);
1140 else if (l_rx.var.evt.prio == 253U) {
1162 if ((i & 1U) != 0U) {
1166 if ((i & 0x80U) != 0U) {
1179 case WAIT4_TEST_SETUP_FRAME: {
1181 l_testData.tpNum = 0U;
1182 l_testData.testTime = 0U;
1188 case WAIT4_TEST_TEARDOWN_FRAME: {
1194 case WAIT4_TEST_CONTINUE_FRAME: {
1200 case WAIT4_TEST_PROBE_FRAME: {
1204 < (
sizeof(l_testData.tpBuf) /
sizeof(l_testData.tpBuf[0])));
1205 l_testData.tpBuf[l_testData.tpNum] = l_rx.var.tp;
1227 case WAIT4_EVT_FRAME: {
1277 std::uint8_t * ptr =
1279 ptr = &ptr[l_rx.var.poke.offs];
1280 switch (l_rx.var.poke.size) {
1282 *ptr =
static_cast<std::uint8_t
>(l_rx.var.poke.data);
1285 *
reinterpret_cast<std::uint16_t *
>(ptr)
1286 =
static_cast<std::uint16_t
>(l_rx.var.poke.data);
1289 *
reinterpret_cast<std::uint32_t *
>(ptr) = l_rx.var.poke.data;
1296 l_rx.var.poke.data = 0U;
1297 l_rx.var.poke.idx = 0U;
1298 l_rx.var.poke.offs +=
static_cast<std::uint16_t
>(l_rx.var.poke.size);
1317 std::uint32_t data = 0U;
1318 for (std::uint8_t i = 0U; i < l_testData.tpNum; ++i) {
1319 if (l_testData.tpBuf[i].addr ==
reinterpret_cast<QSFun
>(api)) {
1320 data = l_testData.tpBuf[i].data;
1335 for (std::uint8_t j = i; j < l_testData.tpNum; ++j) {
1336 l_testData.tpBuf[j] = l_testData.tpBuf[j + 1U];
1346 return (++l_testData.testTime);
QActive active object (based on QP::QHsm implementation)
Native QF Event Queue class.
static void gc(QEvt const *const e) noexcept
Recycle a dynamic event.
static QEvt * newX_(std::uint_fast16_t const evtSize, std::uint_fast16_t const margin, enum_t const sig) noexcept
Internal QF implementation of creating new dynamic event.
static QActive * active_[QF_MAX_ACTIVE+1U]
array of registered active objects
static std::uint_fast16_t poolGetMaxBlockSize(void) noexcept
Obtain the block size of any registered event pools.
static void tickX_(std::uint_fast8_t const tickRate, void const *const sender) noexcept
Processes all armed time events at every clock tick.
static void publish_(QEvt const *const e, void const *const sender, std::uint_fast8_t const qs_id) noexcept
Publish event to the framework.
Hierarchical State Machine base class.
Native QF memory pool class.
void * currObj[MAX_OBJ]
current objects
static struct QP::QS::QSrxPriv rxPriv_
static void setCurrObj(std::uint8_t obj_kind, void *obj_ptr) noexcept
Set the "current object" in the Target.
static QSTimeCtr onGetTime(void)
Callback to obtain a timestamp for a QS record.
static void rxHandleGoodFrame_(std::uint8_t const state)
internal function to handle incoming (QS-RX) packet
QSCtr volatile tail
offset of where next record will be extracted
static void onTestTeardown(void)
callback to teardown after a unit test inside the Target
static std::uint32_t getTestProbe_(void(*const api)(void)) noexcept
internal function to get the Test-Probe for a given API
static void beginRec_(std::uint_fast8_t const rec) noexcept
Mark the begin of a QS record rec.
static void onTestSetup(void)
callback to setup a unit test inside the Target
@ SM_OBJ
state machine object for QEP
@ TE_OBJ
time event object
@ AP_OBJ
generic Application-specific object
@ MP_OBJ
event pool object
std::uint8_t locFilter[16]
lobal on/off QS filter
static void onReset(void)
callback function to reset the Target (to be implemented in the BSP)
static void endRec_(void) noexcept
Mark the end of a QS record rec.
bool inTestLoop
QUTest event loop is running.
static void processTestEvts_(void)
internal function to process posted events during test
QSCtr end
offset of the end of the ring buffer
static std::uint16_t rxGetNfree(void) noexcept
Obtain the number of free bytes in the QS RX data buffer.
static void locFilter_(std::int_fast16_t const filter) noexcept
Set/clear the local Filter for a given object-id.
std::uint8_t glbFilter[16]
global on/off QS filter
QSCtr volatile head
offset to where next byte will be inserted
static void queryCurrObj(std::uint8_t obj_kind) noexcept
Query the "current object" in the Target.
static void onCommand(std::uint8_t cmdId, std::uint32_t param1, std::uint32_t param2, std::uint32_t param3)
Callback function to execute user commands (to be implemented in BSP)
static void rxInitBuf(std::uint8_t *const sto, std::uint16_t const stoSize) noexcept
Initialize the QS RX data buffer.
@ SM_AO_OBJ
combination of SM and AO
static void tickX_(std::uint_fast8_t const tickRate, void const *const sender) noexcept
internal function to process armed time events during test
std::uint8_t * buf
pointer to the start of the ring buffer
static void rxParse(void)
Parse all bytes present in the QS RX data buffer.
static void onTestEvt(QEvt *e)
callback to "massage" the test event before dispatching/posting it
namespace associated with the QP/C++ framework
static void rxParseData_(std::uint8_t const b) noexcept
constexpr std::uint8_t QS_ESC
Escape character of the QS output protocol.
void QS_target_info_(std::uint8_t const isReset) noexcept
send the Target info (object sizes, build time-stamp, QP version)
@ QS_TEST_PROBE_GET
reports that Test-Probe has been used
@ QS_TARGET_DONE
reports completion of a user callback
@ QS_OBJ_DICT
object dictionary entry
@ QS_RX_STATUS
reports QS data receive status
@ QS_QUERY_DATA
reports the data from "current object" query
@ QS_PEEK_DATA
reports the data from the PEEK query
constexpr std::uint8_t QS_ESC_XOR
Escape modifier of the QS output protocol.
constexpr std::uint8_t QS_GOOD_CHKSUM
Escape character of the QS output protocol.
std::uint_fast16_t QSCtr
QS ring buffer counter and offset type.
constexpr std::uint8_t QS_FRAME
Frame character of the QS output protocol.
static void rxReportDone_(enum QSpyRxRecords const recId) noexcept
static void rxReportError_(std::uint8_t const code) noexcept
static void rxPoke_(void) noexcept
QSpyRxRecords
QS received record types (RX channel)
@ QS_RX_RESET
reset the Target
@ QS_RX_EVENT
inject an event to the Target (post/publish)
@ QS_RX_LOC_FILTER
set local filters in the Target
@ QS_RX_FILL
fill Target memory
@ QS_RX_AO_FILTER
set local AO filter in the Target
@ QS_RX_TICK
call QF_tick()
@ QS_RX_PEEK
peek Target memory
@ QS_RX_CURR_OBJ
set the "current-object" in the Target
@ QS_RX_GLB_FILTER
set global filters in the Target
@ QS_RX_POKE
poke Target memory
@ QS_RX_INFO
query Target info (ver, config, tstamp)
@ QS_RX_TEST_TEARDOWN
test teardown
@ QS_RX_TEST_PROBE
set a Test-Probe in the Target
@ QS_RX_TEST_SETUP
test setup
@ QS_RX_COMMAND
execute a user-defined command in the Target
@ QS_RX_TEST_CONTINUE
continue a test after QS_RX_TEST_WAIT()
@ QS_RX_QUERY_CURR
query the "current object" in the Target
static void rxReportAck_(enum QSpyRxRecords const recId) noexcept
std::uint16_t QSignal
QSignal represents the signal of an event.
static void rxHandleBadFrame_(std::uint8_t const state) noexcept
void QF_EVT_REF_CTR_INC_(QEvt const *const e) noexcept
increment the refCtr_ of an event e
Customizable and memory-efficient assertions for embedded systems.
#define Q_DEFINE_THIS_MODULE(name_)
#define Q_ASSERT_ID(id_, test_)
#define Q_REQUIRE_ID(id_, test_)
int enum_t
alias for enumerations used for event signals
#define Q_SIGNAL_SIZE
The size (in bytes) of the signal of an event.
Internal (package scope) QF/C++ interface.
#define QS_CRIT_STAT_
This is an internal macro for defining the critical section status type.
#define QS_REC_DONE()
macro to hook up user code when a QS record is produced
#define QS_CRIT_X_()
This is an internal macro for exiting a critical section.
#define QS_CRIT_E_()
This is an internal macro for entering a critical section.
Internal (package scope) QS/C++ interface.
#define QS_U8_PRE_(data_)
Internal QS macro to output an unformatted uint8_t data element.
#define QS_OBJ_PRE_(obj_)
Internal QS macro to output object pointer data element.
#define QS_FUN_PRE_(fun_)
Internal QS macro to output an unformatted function pointer data element.
#define QS_MPC_PRE_(ctr_)
Internal QS macro to output an unformatted memory pool block-counter data element.
#define QS_TEC_PRE_(ctr_)
Internal QS macro to output an unformatted time event tick-counter data element.
#define QS_U16_PRE_(data_)
Internal QS macro to output an unformatted uint16_t data element.
#define QS_EQC_PRE_(ctr_)
Internal QS macro to output an unformatted event queue counter data element.
#define QS_U32_PRE_(data_)
Internal QS macro to output an unformatted uint32_t data element.
#define QS_STR_PRE_(msg_)
Internal QS macro to output a zero-terminated ASCII string data element.