Adding --trace_ring_buffer.

This commit is contained in:
Ben Vanik 2013-11-16 03:06:55 -08:00
parent 96c27b3b51
commit 271abbf874
3 changed files with 86 additions and 76 deletions

View File

@ -15,5 +15,7 @@
DECLARE_string(gpu); DECLARE_string(gpu);
DECLARE_bool(trace_ring_buffer);
#endif // XENIA_GPU_PRIVATE_H_ #endif // XENIA_GPU_PRIVATE_H_

View File

@ -20,6 +20,10 @@ DEFINE_string(gpu, "any",
"Graphics system. Use: [any, nop, d3d11]"); "Graphics system. Use: [any, nop, d3d11]");
DEFINE_bool(trace_ring_buffer, false,
"Trace GPU ring buffer packets.");
#include <xenia/gpu/nop/nop_gpu.h> #include <xenia/gpu/nop/nop_gpu.h>
GraphicsSystem* xe::gpu::CreateNop(Emulator* emulator) { GraphicsSystem* xe::gpu::CreateNop(Emulator* emulator) {
return xe::gpu::nop::Create(emulator); return xe::gpu::nop::Create(emulator);

View File

@ -9,6 +9,7 @@
#include <xenia/gpu/ring_buffer_worker.h> #include <xenia/gpu/ring_buffer_worker.h>
#include <xenia/gpu/gpu-private.h>
#include <xenia/gpu/graphics_driver.h> #include <xenia/gpu/graphics_driver.h>
#include <xenia/gpu/graphics_system.h> #include <xenia/gpu/graphics_system.h>
#include <xenia/gpu/xenos/packets.h> #include <xenia/gpu/xenos/packets.h>
@ -20,6 +21,9 @@ using namespace xe::gpu;
using namespace xe::gpu::xenos; using namespace xe::gpu::xenos;
#define XETRACERB(fmt, ...) if (FLAGS_trace_ring_buffer) XELOGGPU(fmt, ##__VA_ARGS__)
RingBufferWorker::RingBufferWorker( RingBufferWorker::RingBufferWorker(
GraphicsSystem* graphics_system, xe_memory_ref memory) : GraphicsSystem* graphics_system, xe_memory_ref memory) :
graphics_system_(graphics_system), memory_(memory), driver_(0) { graphics_system_(graphics_system), memory_(memory), driver_(0) {
@ -106,7 +110,7 @@ void RingBufferWorker::Pump() {
} }
// Process the new commands. // Process the new commands.
XELOGGPU("Ring buffer thread work"); XETRACERB("Ring buffer thread work");
// Execute. Note that we handle wraparound transparently. // Execute. Note that we handle wraparound transparently.
ExecutePrimaryBuffer(read_ptr_index_, write_ptr_index); ExecutePrimaryBuffer(read_ptr_index_, write_ptr_index);
@ -127,7 +131,7 @@ void RingBufferWorker::ExecutePrimaryBuffer(
uint32_t end_ptr = primary_buffer_ptr_ + end_index * 4; uint32_t end_ptr = primary_buffer_ptr_ + end_index * 4;
end_ptr = (primary_buffer_ptr_ & ~0x1FFFFFFF) | (end_ptr & 0x1FFFFFFF); end_ptr = (primary_buffer_ptr_ & ~0x1FFFFFFF) | (end_ptr & 0x1FFFFFFF);
XELOGGPU("[%.8X] ExecutePrimaryBuffer(%dw -> %dw)", XETRACERB("[%.8X] ExecutePrimaryBuffer(%dw -> %dw)",
ptr, start_index, end_index); ptr, start_index, end_index);
// Execute commands! // Execute commands!
@ -144,11 +148,11 @@ void RingBufferWorker::ExecutePrimaryBuffer(
XEASSERT(n == (end_index - start_index)); XEASSERT(n == (end_index - start_index));
} }
XELOGGPU(" ExecutePrimaryBuffer End"); XETRACERB(" ExecutePrimaryBuffer End");
} }
void RingBufferWorker::ExecuteIndirectBuffer(uint32_t ptr, uint32_t length) { void RingBufferWorker::ExecuteIndirectBuffer(uint32_t ptr, uint32_t length) {
XELOGGPU("[%.8X] ExecuteIndirectBuffer(%dw)", ptr, length); XETRACERB("[%.8X] ExecuteIndirectBuffer(%dw)", ptr, length);
// Execute commands! // Execute commands!
PacketArgs args; PacketArgs args;
@ -161,12 +165,12 @@ void RingBufferWorker::ExecuteIndirectBuffer(uint32_t ptr, uint32_t length) {
XEASSERT(n <= length); XEASSERT(n <= length);
} }
XELOGGPU(" ExecuteIndirectBuffer End"); XETRACERB(" ExecuteIndirectBuffer End");
} }
#define LOG_DATA(count) \ #define LOG_DATA(count) \
for (uint32_t __m = 0; __m < count; __m++) { \ for (uint32_t __m = 0; __m < count; __m++) { \
XELOGGPU("[%.8X] %.8X", \ XETRACERB("[%.8X] %.8X", \
packet_ptr + (1 + __m) * 4, \ packet_ptr + (1 + __m) * 4, \
XEGETUINT32BE(packet_base + 1 * 4 + __m * 4)); \ XEGETUINT32BE(packet_base + 1 * 4 + __m * 4)); \
} }
@ -194,7 +198,7 @@ uint32_t RingBufferWorker::ExecutePacket(PacketArgs& args) {
ADVANCE_PTR(1); ADVANCE_PTR(1);
const uint32_t packet_type = packet >> 30; const uint32_t packet_type = packet >> 30;
if (packet == 0) { if (packet == 0) {
XELOGGPU("[%.8X] Packet(%.8X): 0?", XETRACERB("[%.8X] Packet(%.8X): 0?",
packet_ptr, packet); packet_ptr, packet);
return 1; return 1;
} }
@ -205,7 +209,7 @@ uint32_t RingBufferWorker::ExecutePacket(PacketArgs& args) {
// Type-0 packet. // Type-0 packet.
// Write count registers in sequence to the registers starting at // Write count registers in sequence to the registers starting at
// (base_index << 2). // (base_index << 2).
XELOGGPU("[%.8X] Packet(%.8X): set registers:", XETRACERB("[%.8X] Packet(%.8X): set registers:",
packet_ptr, packet); packet_ptr, packet);
uint32_t count = ((packet >> 16) & 0x3FFF) + 1; uint32_t count = ((packet >> 16) & 0x3FFF) + 1;
uint32_t base_index = (packet & 0x7FFF); uint32_t base_index = (packet & 0x7FFF);
@ -214,7 +218,7 @@ uint32_t RingBufferWorker::ExecutePacket(PacketArgs& args) {
uint32_t reg_data = PEEK_PTR(); uint32_t reg_data = PEEK_PTR();
uint32_t target_index = write_one_reg ? base_index : base_index + m; uint32_t target_index = write_one_reg ? base_index : base_index + m;
const char* reg_name = xenos::GetRegisterName(target_index); const char* reg_name = xenos::GetRegisterName(target_index);
XELOGGPU("[%.8X] %.8X -> %.4X %s", XETRACERB("[%.8X] %.8X -> %.4X %s",
args.ptr, args.ptr,
reg_data, target_index, reg_name ? reg_name : ""); reg_data, target_index, reg_name ? reg_name : "");
ADVANCE_PTR(1); ADVANCE_PTR(1);
@ -227,7 +231,7 @@ uint32_t RingBufferWorker::ExecutePacket(PacketArgs& args) {
{ {
// Type-1 packet. // Type-1 packet.
// Contains two registers of data. Type-0 should be more common. // Contains two registers of data. Type-0 should be more common.
XELOGGPU("[%.8X] Packet(%.8X): set registers:", XETRACERB("[%.8X] Packet(%.8X): set registers:",
packet_ptr, packet); packet_ptr, packet);
uint32_t reg_index_1 = packet & 0x7FF; uint32_t reg_index_1 = packet & 0x7FF;
uint32_t reg_index_2 = (packet >> 11) & 0x7FF; uint32_t reg_index_2 = (packet >> 11) & 0x7FF;
@ -237,10 +241,10 @@ uint32_t RingBufferWorker::ExecutePacket(PacketArgs& args) {
uint32_t reg_data_2 = READ_PTR(); uint32_t reg_data_2 = READ_PTR();
const char* reg_name_1 = xenos::GetRegisterName(reg_index_1); const char* reg_name_1 = xenos::GetRegisterName(reg_index_1);
const char* reg_name_2 = xenos::GetRegisterName(reg_index_2); const char* reg_name_2 = xenos::GetRegisterName(reg_index_2);
XELOGGPU("[%.8X] %.8X -> %.4X %s", XETRACERB("[%.8X] %.8X -> %.4X %s",
reg_ptr_1, reg_ptr_1,
reg_data_1, reg_index_1, reg_name_1 ? reg_name_1 : ""); reg_data_1, reg_index_1, reg_name_1 ? reg_name_1 : "");
XELOGGPU("[%.8X] %.8X -> %.4X %s", XETRACERB("[%.8X] %.8X -> %.4X %s",
reg_ptr_2, reg_ptr_2,
reg_data_2, reg_index_2, reg_name_2 ? reg_name_2 : ""); reg_data_2, reg_index_2, reg_name_2 ? reg_name_2 : "");
WriteRegister(packet_ptr, reg_index_1, reg_data_1); WriteRegister(packet_ptr, reg_index_1, reg_data_1);
@ -251,7 +255,7 @@ uint32_t RingBufferWorker::ExecutePacket(PacketArgs& args) {
case 0x02: case 0x02:
// Type-2 packet. // Type-2 packet.
// No-op. Do nothing. // No-op. Do nothing.
XELOGGPU("[%.8X] Packet(%.8X): padding", XETRACERB("[%.8X] Packet(%.8X): padding",
packet_ptr, packet); packet_ptr, packet);
return 1; return 1;
case 0x03: case 0x03:
@ -264,7 +268,7 @@ uint32_t RingBufferWorker::ExecutePacket(PacketArgs& args) {
switch (opcode) { switch (opcode) {
case PM4_ME_INIT: case PM4_ME_INIT:
// initialize CP's micro-engine // initialize CP's micro-engine
XELOGGPU("[%.8X] Packet(%.8X): PM4_ME_INIT", XETRACERB("[%.8X] Packet(%.8X): PM4_ME_INIT",
packet_ptr, packet); packet_ptr, packet);
LOG_DATA(count); LOG_DATA(count);
ADVANCE_PTR(count); ADVANCE_PTR(count);
@ -273,7 +277,7 @@ uint32_t RingBufferWorker::ExecutePacket(PacketArgs& args) {
case PM4_NOP: case PM4_NOP:
// skip N 32-bit words to get to the next packet // skip N 32-bit words to get to the next packet
// No-op, ignore some data. // No-op, ignore some data.
XELOGGPU("[%.8X] Packet(%.8X): PM4_NOP", XETRACERB("[%.8X] Packet(%.8X): PM4_NOP",
packet_ptr, packet); packet_ptr, packet);
LOG_DATA(count); LOG_DATA(count);
ADVANCE_PTR(count); ADVANCE_PTR(count);
@ -282,7 +286,7 @@ uint32_t RingBufferWorker::ExecutePacket(PacketArgs& args) {
case PM4_INTERRUPT: case PM4_INTERRUPT:
// generate interrupt from the command stream // generate interrupt from the command stream
{ {
XELOGGPU("[%.8X] Packet(%.8X): PM4_INTERRUPT", XETRACERB("[%.8X] Packet(%.8X): PM4_INTERRUPT",
packet_ptr, packet); packet_ptr, packet);
LOG_DATA(count); LOG_DATA(count);
uint32_t cpu_mask = READ_PTR(); uint32_t cpu_mask = READ_PTR();
@ -299,7 +303,7 @@ uint32_t RingBufferWorker::ExecutePacket(PacketArgs& args) {
{ {
uint32_t list_ptr = READ_PTR(); uint32_t list_ptr = READ_PTR();
uint32_t list_length = READ_PTR(); uint32_t list_length = READ_PTR();
XELOGGPU("[%.8X] Packet(%.8X): PM4_INDIRECT_BUFFER %.8X (%dw)", XETRACERB("[%.8X] Packet(%.8X): PM4_INDIRECT_BUFFER %.8X (%dw)",
packet_ptr, packet, list_ptr, list_length); packet_ptr, packet, list_ptr, list_length);
ExecuteIndirectBuffer(GpuToCpu(list_ptr), list_length); ExecuteIndirectBuffer(GpuToCpu(list_ptr), list_length);
} }
@ -308,7 +312,7 @@ uint32_t RingBufferWorker::ExecutePacket(PacketArgs& args) {
case PM4_WAIT_REG_MEM: case PM4_WAIT_REG_MEM:
// wait until a register or memory location is a specific value // wait until a register or memory location is a specific value
{ {
XELOGGPU("[%.8X] Packet(%.8X): PM4_WAIT_REG_MEM", XETRACERB("[%.8X] Packet(%.8X): PM4_WAIT_REG_MEM",
packet_ptr, packet); packet_ptr, packet);
LOG_DATA(count); LOG_DATA(count);
uint32_t wait_info = READ_PTR(); uint32_t wait_info = READ_PTR();
@ -372,7 +376,7 @@ uint32_t RingBufferWorker::ExecutePacket(PacketArgs& args) {
// register read/modify/write // register read/modify/write
// ? (used during shader upload and edram setup) // ? (used during shader upload and edram setup)
{ {
XELOGGPU("[%.8X] Packet(%.8X): PM4_REG_RMW", XETRACERB("[%.8X] Packet(%.8X): PM4_REG_RMW",
packet_ptr, packet); packet_ptr, packet);
LOG_DATA(count); LOG_DATA(count);
uint32_t rmw_info = READ_PTR(); uint32_t rmw_info = READ_PTR();
@ -400,7 +404,7 @@ uint32_t RingBufferWorker::ExecutePacket(PacketArgs& args) {
case PM4_COND_WRITE: case PM4_COND_WRITE:
// conditional write to memory or register // conditional write to memory or register
{ {
XELOGGPU("[%.8X] Packet(%.8X): PM4_COND_WRITE", XETRACERB("[%.8X] Packet(%.8X): PM4_COND_WRITE",
packet_ptr, packet); packet_ptr, packet);
LOG_DATA(count); LOG_DATA(count);
uint32_t wait_info = READ_PTR(); uint32_t wait_info = READ_PTR();
@ -468,7 +472,7 @@ uint32_t RingBufferWorker::ExecutePacket(PacketArgs& args) {
case PM4_EVENT_WRITE: case PM4_EVENT_WRITE:
// generate an event that creates a write to memory when completed // generate an event that creates a write to memory when completed
{ {
XELOGGPU("[%.8X] Packet(%.8X): PM4_EVENT_WRITE (unimplemented!)", XETRACERB("[%.8X] Packet(%.8X): PM4_EVENT_WRITE (unimplemented!)",
packet_ptr, packet); packet_ptr, packet);
LOG_DATA(count); LOG_DATA(count);
uint32_t initiator = READ_PTR(); uint32_t initiator = READ_PTR();
@ -484,7 +488,7 @@ uint32_t RingBufferWorker::ExecutePacket(PacketArgs& args) {
case PM4_EVENT_WRITE_SHD: case PM4_EVENT_WRITE_SHD:
// generate a VS|PS_done event // generate a VS|PS_done event
{ {
XELOGGPU("[%.8X] Packet(%.8X): PM4_EVENT_WRITE_SHD", XETRACERB("[%.8X] Packet(%.8X): PM4_EVENT_WRITE_SHD",
packet_ptr, packet); packet_ptr, packet);
LOG_DATA(count); LOG_DATA(count);
uint32_t initiator = READ_PTR(); uint32_t initiator = READ_PTR();
@ -511,7 +515,7 @@ uint32_t RingBufferWorker::ExecutePacket(PacketArgs& args) {
case PM4_DRAW_INDX: case PM4_DRAW_INDX:
// initiate fetch of index buffer and draw // initiate fetch of index buffer and draw
{ {
XELOGGPU("[%.8X] Packet(%.8X): PM4_DRAW_INDX", XETRACERB("[%.8X] Packet(%.8X): PM4_DRAW_INDX",
packet_ptr, packet); packet_ptr, packet);
LOG_DATA(count); LOG_DATA(count);
// d0 = viz query info // d0 = viz query info
@ -543,7 +547,7 @@ uint32_t RingBufferWorker::ExecutePacket(PacketArgs& args) {
case PM4_DRAW_INDX_2: case PM4_DRAW_INDX_2:
// draw using supplied indices in packet // draw using supplied indices in packet
{ {
XELOGGPU("[%.8X] Packet(%.8X): PM4_DRAW_INDX_2", XETRACERB("[%.8X] Packet(%.8X): PM4_DRAW_INDX_2",
packet_ptr, packet); packet_ptr, packet);
LOG_DATA(count); LOG_DATA(count);
uint32_t d0 = READ_PTR(); uint32_t d0 = READ_PTR();
@ -560,7 +564,7 @@ uint32_t RingBufferWorker::ExecutePacket(PacketArgs& args) {
case PM4_SET_CONSTANT: case PM4_SET_CONSTANT:
// load constant into chip and to memory // load constant into chip and to memory
{ {
XELOGGPU("[%.8X] Packet(%.8X): PM4_SET_CONSTANT", XETRACERB("[%.8X] Packet(%.8X): PM4_SET_CONSTANT",
packet_ptr, packet); packet_ptr, packet);
// PM4_REG(reg) ((0x4 << 16) | (GSL_HAL_SUBBLOCK_OFFSET(reg))) // PM4_REG(reg) ((0x4 << 16) | (GSL_HAL_SUBBLOCK_OFFSET(reg)))
// reg - 0x2000 // reg - 0x2000
@ -573,7 +577,7 @@ uint32_t RingBufferWorker::ExecutePacket(PacketArgs& args) {
for (uint32_t n = 0; n < count - 1; n++, index++) { for (uint32_t n = 0; n < count - 1; n++, index++) {
uint32_t data = READ_PTR(); uint32_t data = READ_PTR();
const char* reg_name = xenos::GetRegisterName(index); const char* reg_name = xenos::GetRegisterName(index);
XELOGGPU("[%.8X] %.8X -> %.4X %s", XETRACERB("[%.8X] %.8X -> %.4X %s",
packet_ptr + (1 + n) * 4, packet_ptr + (1 + n) * 4,
data, index, reg_name ? reg_name : ""); data, index, reg_name ? reg_name : "");
WriteRegister(packet_ptr, index, data); WriteRegister(packet_ptr, index, data);
@ -588,7 +592,7 @@ uint32_t RingBufferWorker::ExecutePacket(PacketArgs& args) {
case PM4_LOAD_ALU_CONSTANT: case PM4_LOAD_ALU_CONSTANT:
// load constants from memory // load constants from memory
{ {
XELOGGPU("[%.8X] Packet(%.8X): PM4_LOAD_ALU_CONSTANT", XETRACERB("[%.8X] Packet(%.8X): PM4_LOAD_ALU_CONSTANT",
packet_ptr, packet); packet_ptr, packet);
uint32_t address = READ_PTR(); uint32_t address = READ_PTR();
address &= 0x3FFFFFFF; address &= 0x3FFFFFFF;
@ -601,7 +605,7 @@ uint32_t RingBufferWorker::ExecutePacket(PacketArgs& args) {
uint32_t data = XEGETUINT32BE( uint32_t data = XEGETUINT32BE(
p + GpuToCpu(packet_ptr, address + n * 4)); p + GpuToCpu(packet_ptr, address + n * 4));
const char* reg_name = xenos::GetRegisterName(index); const char* reg_name = xenos::GetRegisterName(index);
XELOGGPU("[%.8X] %.8X -> %.4X %s", XETRACERB("[%.8X] %.8X -> %.4X %s",
packet_ptr, packet_ptr,
data, index, reg_name ? reg_name : ""); data, index, reg_name ? reg_name : "");
WriteRegister(packet_ptr, index, data); WriteRegister(packet_ptr, index, data);
@ -612,7 +616,7 @@ uint32_t RingBufferWorker::ExecutePacket(PacketArgs& args) {
case PM4_IM_LOAD: case PM4_IM_LOAD:
// load sequencer instruction memory (pointer-based) // load sequencer instruction memory (pointer-based)
{ {
XELOGGPU("[%.8X] Packet(%.8X): PM4_IM_LOAD", XETRACERB("[%.8X] Packet(%.8X): PM4_IM_LOAD",
packet_ptr, packet); packet_ptr, packet);
LOG_DATA(count); LOG_DATA(count);
uint32_t addr_type = READ_PTR(); uint32_t addr_type = READ_PTR();
@ -632,7 +636,7 @@ uint32_t RingBufferWorker::ExecutePacket(PacketArgs& args) {
case PM4_IM_LOAD_IMMEDIATE: case PM4_IM_LOAD_IMMEDIATE:
// load sequencer instruction memory (code embedded in packet) // load sequencer instruction memory (code embedded in packet)
{ {
XELOGGPU("[%.8X] Packet(%.8X): PM4_IM_LOAD_IMMEDIATE", XETRACERB("[%.8X] Packet(%.8X): PM4_IM_LOAD_IMMEDIATE",
packet_ptr, packet); packet_ptr, packet);
LOG_DATA(count); LOG_DATA(count);
uint32_t type = READ_PTR(); uint32_t type = READ_PTR();
@ -654,7 +658,7 @@ uint32_t RingBufferWorker::ExecutePacket(PacketArgs& args) {
case PM4_INVALIDATE_STATE: case PM4_INVALIDATE_STATE:
// selective invalidation of state pointers // selective invalidation of state pointers
{ {
XELOGGPU("[%.8X] Packet(%.8X): PM4_INVALIDATE_STATE", XETRACERB("[%.8X] Packet(%.8X): PM4_INVALIDATE_STATE",
packet_ptr, packet); packet_ptr, packet);
LOG_DATA(count); LOG_DATA(count);
uint32_t mask = READ_PTR(); uint32_t mask = READ_PTR();
@ -665,42 +669,42 @@ uint32_t RingBufferWorker::ExecutePacket(PacketArgs& args) {
case PM4_SET_BIN_MASK_LO: case PM4_SET_BIN_MASK_LO:
{ {
uint32_t value = READ_PTR(); uint32_t value = READ_PTR();
XELOGGPU("[%.8X] Packet(%.8X): PM4_SET_BIN_MASK_LO = %.8X", XETRACERB("[%.8X] Packet(%.8X): PM4_SET_BIN_MASK_LO = %.8X",
packet_ptr, packet, value); packet_ptr, packet, value);
} }
break; break;
case PM4_SET_BIN_MASK_HI: case PM4_SET_BIN_MASK_HI:
{ {
uint32_t value = READ_PTR(); uint32_t value = READ_PTR();
XELOGGPU("[%.8X] Packet(%.8X): PM4_SET_BIN_MASK_HI = %.8X", XETRACERB("[%.8X] Packet(%.8X): PM4_SET_BIN_MASK_HI = %.8X",
packet_ptr, packet, value); packet_ptr, packet, value);
} }
break; break;
case PM4_SET_BIN_SELECT_LO: case PM4_SET_BIN_SELECT_LO:
{ {
uint32_t value = READ_PTR(); uint32_t value = READ_PTR();
XELOGGPU("[%.8X] Packet(%.8X): PM4_SET_BIN_SELECT_LO = %.8X", XETRACERB("[%.8X] Packet(%.8X): PM4_SET_BIN_SELECT_LO = %.8X",
packet_ptr, packet, value); packet_ptr, packet, value);
} }
break; break;
case PM4_SET_BIN_SELECT_HI: case PM4_SET_BIN_SELECT_HI:
{ {
uint32_t value = READ_PTR(); uint32_t value = READ_PTR();
XELOGGPU("[%.8X] Packet(%.8X): PM4_SET_BIN_SELECT_HI = %.8X", XETRACERB("[%.8X] Packet(%.8X): PM4_SET_BIN_SELECT_HI = %.8X",
packet_ptr, packet, value); packet_ptr, packet, value);
} }
break; break;
// Ignored packets - useful if breaking on the default handler below. // Ignored packets - useful if breaking on the default handler below.
case 0x50: // 0xC0015000 usually 2 words, 0xFFFFFFFF / 0x00000000 case 0x50: // 0xC0015000 usually 2 words, 0xFFFFFFFF / 0x00000000
XELOGGPU("[%.8X] Packet(%.8X): unknown!", XETRACERB("[%.8X] Packet(%.8X): unknown!",
packet_ptr, packet); packet_ptr, packet);
LOG_DATA(count); LOG_DATA(count);
ADVANCE_PTR(count); ADVANCE_PTR(count);
break; break;
default: default:
XELOGGPU("[%.8X] Packet(%.8X): unknown!", XETRACERB("[%.8X] Packet(%.8X): unknown!",
packet_ptr, packet); packet_ptr, packet);
LOG_DATA(count); LOG_DATA(count);
ADVANCE_PTR(count); ADVANCE_PTR(count);