mirror of
https://github.com/holub/mame
synced 2025-05-21 13:18:56 +03:00
discrete sound system
- some more "list-ification" - No more constraints on number of input and output nodes - input nodes now make use of a context Some of these changes are needed to introduce "internally buffered" input nodes going forward. These will use an internal stream to buffer all inputs so that stream_update will always calculate 20ms of samples.
This commit is contained in:
parent
98a7eb3c8d
commit
68d94c232d
@ -31,6 +31,18 @@ struct dss_adjustment_context
|
||||
double scale;
|
||||
};
|
||||
|
||||
struct dss_input_context
|
||||
{
|
||||
stream_sample_t *ptr; /* current in ptr for stream */
|
||||
UINT8 data; /* data written */
|
||||
double gain; /* node gain */
|
||||
double offset; /* node offset */
|
||||
UINT8 is_stream;
|
||||
UINT8 is_buffered;
|
||||
UINT32 stream_in_number;
|
||||
UINT32 stream_out_number;
|
||||
};
|
||||
|
||||
INLINE discrete_info *get_safe_token(const device_config *device)
|
||||
{
|
||||
assert(device != NULL);
|
||||
@ -50,14 +62,14 @@ READ8_DEVICE_HANDLER(discrete_sound_r)
|
||||
/* Read the node input value if allowed */
|
||||
if (node)
|
||||
{
|
||||
UINT8 *node_data = (UINT8 *)node->context;
|
||||
struct dss_input_context *context = (struct dss_input_context *)node->context;
|
||||
|
||||
/* Bring the system up to now */
|
||||
stream_update(info->discrete_stream);
|
||||
|
||||
if ((node->module->type >= DSS_INPUT_DATA) && (node->module->type <= DSS_INPUT_PULSE))
|
||||
{
|
||||
data = *node_data;
|
||||
data = context->data;
|
||||
}
|
||||
}
|
||||
else
|
||||
@ -75,8 +87,7 @@ WRITE8_DEVICE_HANDLER(discrete_sound_w)
|
||||
/* Update the node input value if it's a proper input node */
|
||||
if (node)
|
||||
{
|
||||
UINT8 *node_data = (UINT8 *)node->context;
|
||||
UINT8 last_data = *node_data;
|
||||
struct dss_input_context *context = (struct dss_input_context *)node->context;
|
||||
UINT8 new_data = 0;
|
||||
|
||||
switch (node->module->type)
|
||||
@ -93,15 +104,15 @@ WRITE8_DEVICE_HANDLER(discrete_sound_w)
|
||||
break;
|
||||
}
|
||||
|
||||
if (last_data != new_data)
|
||||
if (context->data != new_data)
|
||||
{
|
||||
/* Bring the system up to now */
|
||||
stream_update(info->discrete_stream);
|
||||
|
||||
*node_data = new_data;
|
||||
context->data = new_data;
|
||||
|
||||
/* Update the node output here so we don't have to do it each step */
|
||||
node->output[0] = *node_data * DSS_INPUT__GAIN + DSS_INPUT__OFFSET;
|
||||
node->output[0] = new_data * context->gain + context->offset;
|
||||
}
|
||||
}
|
||||
else
|
||||
@ -217,33 +228,38 @@ static DISCRETE_RESET(dss_constant)
|
||||
************************************************************************/
|
||||
static DISCRETE_RESET(dss_input)
|
||||
{
|
||||
UINT8 *node_data = (UINT8 *)node->context;
|
||||
struct dss_input_context *context = (struct dss_input_context *)node->context;
|
||||
|
||||
context->is_buffered = FALSE;
|
||||
context->is_stream = FALSE;
|
||||
context->gain = DSS_INPUT__GAIN;
|
||||
context->offset = DSS_INPUT__OFFSET;
|
||||
|
||||
switch (node->module->type)
|
||||
{
|
||||
case DSS_INPUT_DATA:
|
||||
*node_data = DSS_INPUT__INIT;
|
||||
context->data = DSS_INPUT__INIT;
|
||||
break;
|
||||
case DSS_INPUT_LOGIC:
|
||||
case DSS_INPUT_PULSE:
|
||||
*node_data = (DSS_INPUT__INIT == 0) ? 0 : 1;
|
||||
context->data = (DSS_INPUT__INIT == 0) ? 0 : 1;
|
||||
break;
|
||||
case DSS_INPUT_NOT:
|
||||
*node_data = (DSS_INPUT__INIT == 0) ? 1 : 0;
|
||||
context->data = (DSS_INPUT__INIT == 0) ? 1 : 0;
|
||||
break;
|
||||
}
|
||||
node->output[0] = *node_data * DSS_INPUT__GAIN + DSS_INPUT__OFFSET;
|
||||
node->output[0] = context->data * context->gain + context->offset;
|
||||
}
|
||||
|
||||
static DISCRETE_STEP(dss_input_pulse)
|
||||
{
|
||||
UINT8 *node_data = (UINT8 *)node->context;
|
||||
struct dss_input_context *context = (struct dss_input_context *)node->context;
|
||||
|
||||
/* Set a valid output */
|
||||
node->output[0] = *node_data;
|
||||
node->output[0] = context->data;
|
||||
/* Reset the input to default for the next cycle */
|
||||
/* node order is now important */
|
||||
*node_data = DSS_INPUT__INIT;
|
||||
context->data = DSS_INPUT__INIT;
|
||||
}
|
||||
|
||||
|
||||
@ -263,13 +279,12 @@ static DISCRETE_STEP(dss_input_pulse)
|
||||
static DISCRETE_STEP(dss_input_stream)
|
||||
{
|
||||
/* the context pointer is set to point to the current input stream data in discrete_stream_update */
|
||||
stream_sample_t **ptr = (stream_sample_t **)node->context;
|
||||
stream_sample_t *data = *ptr;
|
||||
struct dss_input_context *context = (struct dss_input_context *)node->context;
|
||||
|
||||
if (data)
|
||||
if (context->ptr)
|
||||
{
|
||||
node->output[0] = (*data) * DSS_INPUT_STREAM__GAIN + DSS_INPUT_STREAM__OFFSET;
|
||||
(*ptr)++;
|
||||
node->output[0] = (*context->ptr) * context->gain + context->offset;
|
||||
context->ptr++;
|
||||
}
|
||||
else
|
||||
node->output[0] = 0;
|
||||
@ -277,8 +292,13 @@ static DISCRETE_STEP(dss_input_stream)
|
||||
|
||||
static DISCRETE_RESET(dss_input_stream)
|
||||
{
|
||||
int istream = DSS_INPUT_STREAM__STREAM;
|
||||
/* we will use the node's context pointer to point to the input stream data */
|
||||
assert(istream < node->info->discrete_input_streams);
|
||||
node->context = (discrete_info *) &node->info->input_stream_data[istream];
|
||||
struct dss_input_context *context = (struct dss_input_context *)node->context;
|
||||
|
||||
assert(DSS_INPUT_STREAM__STREAM < linked_list_count(node->info->input_list));
|
||||
context->is_buffered = FALSE;
|
||||
context->is_stream = TRUE;
|
||||
context->stream_in_number = DSS_INPUT_STREAM__STREAM;
|
||||
context->gain = DSS_INPUT_STREAM__GAIN;
|
||||
context->offset = DSS_INPUT_STREAM__OFFSET;
|
||||
context->ptr = NULL;
|
||||
}
|
||||
|
@ -169,6 +169,39 @@ static DISCRETE_RESET( dso_output )
|
||||
/* nothing to do - just avoid being stepped */
|
||||
}
|
||||
|
||||
|
||||
/*************************************
|
||||
*
|
||||
* Add an entry to a list
|
||||
*
|
||||
*************************************/
|
||||
|
||||
static void linked_list_add(discrete_info *info, linked_list_entry ***list_tail_ptr, void *ptr)
|
||||
{
|
||||
**list_tail_ptr = auto_alloc(info->device->machine, linked_list_entry);
|
||||
(**list_tail_ptr)->ptr = ptr;
|
||||
(**list_tail_ptr)->next = NULL;
|
||||
*list_tail_ptr = &((**list_tail_ptr)->next);
|
||||
}
|
||||
|
||||
/*************************************
|
||||
*
|
||||
* Count entries in a list
|
||||
*
|
||||
*************************************/
|
||||
|
||||
static int linked_list_count(linked_list_entry *list)
|
||||
{
|
||||
int cnt = 0;
|
||||
linked_list_entry *entry;
|
||||
|
||||
for (entry = list; entry != NULL; entry = entry->next)
|
||||
cnt++;
|
||||
|
||||
return cnt;
|
||||
}
|
||||
|
||||
|
||||
/*************************************
|
||||
*
|
||||
* Included simulation objects
|
||||
@ -191,7 +224,7 @@ static DISCRETE_RESET( dso_output )
|
||||
|
||||
static const discrete_module module_list[] =
|
||||
{
|
||||
{ DSO_OUTPUT ,"DSO_OUTPUT" , 0 ,sizeof(0) ,dso_output_reset ,dso_output_step },
|
||||
{ DSO_OUTPUT ,"DSO_OUTPUT" , 0 ,0 ,dso_output_reset ,dso_output_step },
|
||||
{ DSO_CSVLOG ,"DSO_CSVLOG" , 0 ,0 ,NULL ,NULL },
|
||||
{ DSO_WAVELOG ,"DSO_WAVELOG" , 0 ,0 ,NULL ,NULL },
|
||||
{ DSO_IMPORT ,"DSO_IMPORT" , 0 ,0 ,NULL ,NULL },
|
||||
@ -207,11 +240,11 @@ static const discrete_module module_list[] =
|
||||
/* from disc_inp.c */
|
||||
{ DSS_ADJUSTMENT ,"DSS_ADJUSTMENT" , 1 ,sizeof(struct dss_adjustment_context) ,dss_adjustment_reset ,dss_adjustment_step },
|
||||
{ DSS_CONSTANT ,"DSS_CONSTANT" , 1 ,0 ,dss_constant_reset ,NULL },
|
||||
{ DSS_INPUT_DATA ,"DSS_INPUT_DATA" , 1 ,sizeof(UINT8) ,dss_input_reset ,NULL },
|
||||
{ DSS_INPUT_LOGIC ,"DSS_INPUT_LOGIC" , 1 ,sizeof(UINT8) ,dss_input_reset ,NULL },
|
||||
{ DSS_INPUT_NOT ,"DSS_INPUT_NOT" , 1 ,sizeof(UINT8) ,dss_input_reset ,NULL },
|
||||
{ DSS_INPUT_PULSE ,"DSS_INPUT_PULSE" , 1 ,sizeof(UINT8) ,dss_input_reset ,dss_input_pulse_step },
|
||||
{ DSS_INPUT_STREAM,"DSS_INPUT_STREAM", 1 ,0 ,dss_input_stream_reset,dss_input_stream_step},
|
||||
{ DSS_INPUT_DATA ,"DSS_INPUT_DATA" , 1 ,sizeof(struct dss_input_context) ,dss_input_reset ,NULL },
|
||||
{ DSS_INPUT_LOGIC ,"DSS_INPUT_LOGIC" , 1 ,sizeof(struct dss_input_context) ,dss_input_reset ,NULL },
|
||||
{ DSS_INPUT_NOT ,"DSS_INPUT_NOT" , 1 ,sizeof(struct dss_input_context) ,dss_input_reset ,NULL },
|
||||
{ DSS_INPUT_PULSE ,"DSS_INPUT_PULSE" , 1 ,sizeof(struct dss_input_context) ,dss_input_reset ,dss_input_pulse_step },
|
||||
{ DSS_INPUT_STREAM,"DSS_INPUT_STREAM", 1 ,sizeof(struct dss_input_context) ,dss_input_stream_reset,dss_input_stream_step},
|
||||
|
||||
/* from disc_wav.c */
|
||||
/* Generic modules */
|
||||
@ -304,20 +337,6 @@ static const discrete_module module_list[] =
|
||||
{ DSS_NULL ,"DSS_NULL" , 0 ,0 ,NULL ,NULL }
|
||||
};
|
||||
|
||||
/*************************************
|
||||
*
|
||||
* Add an entry to a list
|
||||
*
|
||||
*************************************/
|
||||
|
||||
static void add_list(discrete_info *info, linked_list_entry ***list, void *ptr)
|
||||
{
|
||||
**list = auto_alloc(info->device->machine, linked_list_entry);
|
||||
(**list)->ptr = ptr;
|
||||
(**list)->next = NULL;
|
||||
*list = &((**list)->next);
|
||||
}
|
||||
|
||||
/*************************************
|
||||
*
|
||||
* Find a given node
|
||||
@ -396,7 +415,7 @@ static void discrete_build_list(discrete_info *info, discrete_sound_block *intf,
|
||||
else
|
||||
{
|
||||
discrete_log(info, "discrete_build_list() - adding node %d (*current %p)\n", node_count, *current);
|
||||
add_list(info, current, &intf[node_count]);
|
||||
linked_list_add(info, current, &intf[node_count]);
|
||||
}
|
||||
|
||||
node_count++;
|
||||
@ -482,6 +501,8 @@ static DEVICE_START( discrete )
|
||||
/* Start with empty lists */
|
||||
info->node_list = NULL;
|
||||
info->step_list = NULL;
|
||||
info->output_list = NULL;
|
||||
info->input_list = NULL;
|
||||
|
||||
/* allocate memory to hold pointers to nodes by index */
|
||||
info->indexed_node = auto_alloc_array_clear(device->machine, node_description *, DISCRETE_MAX_NODES);
|
||||
@ -494,7 +515,7 @@ static DEVICE_START( discrete )
|
||||
|
||||
/* then set up the output nodes */
|
||||
/* initialize the stream(s) */
|
||||
info->discrete_stream = stream_create(device, info->discrete_input_streams, info->discrete_outputs, info->sample_rate, info, discrete_stream_update);
|
||||
info->discrete_stream = stream_create(device,linked_list_count(info->input_list), linked_list_count(info->output_list), info->sample_rate, info, discrete_stream_update);
|
||||
|
||||
/* allocate a queue */
|
||||
|
||||
@ -749,22 +770,22 @@ static STREAM_UPDATE( discrete_stream_update )
|
||||
{
|
||||
discrete_info *info = (discrete_info *)param;
|
||||
linked_list_entry *entry;
|
||||
int samplenum, nodenum, outputnum;
|
||||
//int j; int left; int run;
|
||||
int samplenum, outputnum;
|
||||
|
||||
if (samples == 0)
|
||||
return;
|
||||
|
||||
/* Setup any output streams */
|
||||
for (outputnum = 0; outputnum < info->discrete_outputs; outputnum++)
|
||||
for (entry = info->output_list, outputnum = 0; entry != NULL; entry = entry->next, outputnum++)
|
||||
{
|
||||
info->output_node[outputnum]->context = (void *) outputs[outputnum];
|
||||
((node_description *) entry->ptr)->context = (void *) outputs[outputnum];
|
||||
}
|
||||
|
||||
/* Setup any input streams */
|
||||
for (nodenum = 0; nodenum < info->discrete_input_streams; nodenum++)
|
||||
for (entry = info->input_list; entry != NULL; entry = entry->next)
|
||||
{
|
||||
info->input_stream_data[nodenum] = inputs[nodenum];
|
||||
struct dss_input_context *context = (struct dss_input_context *) ((node_description *) entry->ptr)->context;
|
||||
context->ptr = (void *) inputs[context->stream_in_number];
|
||||
}
|
||||
|
||||
for (entry = info->task_list; entry != 0; entry = entry->next)
|
||||
@ -812,17 +833,15 @@ static STREAM_UPDATE( discrete_stream_update )
|
||||
|
||||
static void init_nodes(discrete_info *info, linked_list_entry *block_list, const device_config *device)
|
||||
{
|
||||
linked_list_entry **step_list = &info->step_list;
|
||||
linked_list_entry **node_list = &info->node_list;
|
||||
linked_list_entry **task_list = &info->task_list;
|
||||
linked_list_entry **cur_task_node = NULL;
|
||||
linked_list_entry *entry;
|
||||
discrete_task_context *task = NULL;
|
||||
|
||||
/* start with no outputs or input streams */
|
||||
|
||||
info->discrete_outputs = 0;
|
||||
info->discrete_input_streams = 0;
|
||||
/* list tail pointers */
|
||||
linked_list_entry **step_list = &info->step_list;
|
||||
linked_list_entry **node_list = &info->node_list;
|
||||
linked_list_entry **task_list = &info->task_list;
|
||||
linked_list_entry **output_list = &info->output_list;
|
||||
linked_list_entry **input_list = &info->input_list;
|
||||
|
||||
/* loop over all nodes */
|
||||
for (entry = block_list; entry != NULL; entry = entry->next)
|
||||
@ -854,9 +873,7 @@ static void init_nodes(discrete_info *info, linked_list_entry *block_list, const
|
||||
{
|
||||
/* Output Node */
|
||||
case DSO_OUTPUT:
|
||||
if (info->discrete_outputs == DISCRETE_MAX_OUTPUTS)
|
||||
fatalerror("init_nodes() - There can not be more then %d output nodes", DISCRETE_MAX_OUTPUTS);
|
||||
info->output_node[info->discrete_outputs++] = node;
|
||||
linked_list_add(info, &output_list, node);
|
||||
break;
|
||||
|
||||
/* CSVlog Node for debugging */
|
||||
@ -878,7 +895,7 @@ static void init_nodes(discrete_info *info, linked_list_entry *block_list, const
|
||||
if (cur_task_node != NULL)
|
||||
fatalerror("init_nodes() - Nested DISCRETE_START_TASK.");
|
||||
task = auto_alloc_clear(info->device->machine, discrete_task_context);
|
||||
add_list(info, &task_list, task);
|
||||
linked_list_add(info, &task_list, task);
|
||||
cur_task_node = &task->list;
|
||||
break;
|
||||
|
||||
@ -924,15 +941,11 @@ static void init_nodes(discrete_info *info, linked_list_entry *block_list, const
|
||||
/* if we are an stream input node, track that */
|
||||
if (block->type == DSS_INPUT_STREAM)
|
||||
{
|
||||
if (info->discrete_input_streams == DISCRETE_MAX_OUTPUTS)
|
||||
fatalerror("init_nodes() - There can not be more then %d input stream nodes", DISCRETE_MAX_OUTPUTS);
|
||||
|
||||
node->context = NULL;
|
||||
info->discrete_input_streams++;
|
||||
linked_list_add(info, &input_list, node);
|
||||
}
|
||||
|
||||
/* add to node list */
|
||||
add_list(info, &node_list, node);
|
||||
linked_list_add(info, &node_list, node);
|
||||
|
||||
/* our running order just follows the order specified */
|
||||
/* does the node step ? */
|
||||
@ -940,9 +953,9 @@ static void init_nodes(discrete_info *info, linked_list_entry *block_list, const
|
||||
{
|
||||
/* do we belong to a task? */
|
||||
if (cur_task_node == NULL)
|
||||
add_list(info, &step_list, node);
|
||||
linked_list_add(info, &step_list, node);
|
||||
else
|
||||
add_list(info, &cur_task_node, node);
|
||||
linked_list_add(info, &cur_task_node, node);
|
||||
}
|
||||
|
||||
if (block->type == DSO_TASK_END)
|
||||
@ -956,7 +969,7 @@ static void init_nodes(discrete_info *info, linked_list_entry *block_list, const
|
||||
}
|
||||
|
||||
/* if no outputs, give an error */
|
||||
if (info->discrete_outputs == 0)
|
||||
if (linked_list_count(info->output_list) == 0)
|
||||
fatalerror("init_nodes() - Couldn't find an output node");
|
||||
}
|
||||
|
||||
|
@ -438,8 +438,12 @@
|
||||
* Note: The discrete system is floating point based. So when routing a stream
|
||||
* set it's gain to 100% and then use DISCRETE_INPUTX_STREAM to adjust
|
||||
* it if needed.
|
||||
* If you need to access a stream from a discrete task, the stream node
|
||||
* must be part of that task. If a given stream is used in two tasks or
|
||||
* a task and the main task, you must declare two stream nodes acccessing the
|
||||
* same stream input NUM.
|
||||
*
|
||||
* EXAMPLES: see
|
||||
* EXAMPLES: see scramble, frogger
|
||||
*
|
||||
***********************************************************************
|
||||
=======================================================================
|
||||
@ -3371,10 +3375,9 @@
|
||||
|
||||
#define DISCRETE_MAX_NODES 300
|
||||
#define DISCRETE_MAX_INPUTS 10
|
||||
#define DISCRETE_MAX_OUTPUTS 16
|
||||
#define DISCRETE_MAX_WAVELOGS 10
|
||||
#define DISCRETE_MAX_CSVLOGS 10
|
||||
#define DISCRETE_MAX_NODE_OUTPUTS 8
|
||||
#define DISCRETE_MAX_OUTPUTS 8
|
||||
|
||||
|
||||
/*************************************
|
||||
@ -3625,7 +3628,7 @@ struct _discrete_module
|
||||
struct _node_description
|
||||
{
|
||||
int node; /* The node's index number in the node list */
|
||||
double output[DISCRETE_MAX_NODE_OUTPUTS]; /* The node's last output value */
|
||||
double output[DISCRETE_MAX_OUTPUTS]; /* The node's last output value */
|
||||
|
||||
int active_inputs; /* Number of active inputs on this node type */
|
||||
int input_is_node; /* Bit Flags. 1 in bit location means input_is_node */
|
||||
@ -3667,7 +3670,6 @@ struct _discrete_task_context
|
||||
double *ptr;
|
||||
double node_buf[2048];
|
||||
double **dest;
|
||||
volatile INT32 active;
|
||||
};
|
||||
|
||||
struct _discrete_info
|
||||
@ -3695,12 +3697,10 @@ struct _discrete_info
|
||||
linked_list_entry *task_list; /* discrete_task_context * */
|
||||
|
||||
/* the input streams */
|
||||
int discrete_input_streams;
|
||||
stream_sample_t *input_stream_data[DISCRETE_MAX_OUTPUTS];
|
||||
linked_list_entry *input_list;
|
||||
|
||||
/* output node tracking */
|
||||
int discrete_outputs;
|
||||
node_description *output_node[DISCRETE_MAX_OUTPUTS];
|
||||
linked_list_entry *output_list;
|
||||
|
||||
/* the output stream */
|
||||
sound_stream *discrete_stream;
|
||||
@ -4024,10 +4024,10 @@ struct _discrete_inverter_osc_desc
|
||||
*
|
||||
*************************************/
|
||||
|
||||
#define NODE0_DEF(_x) NODE_ ## 0 ## _x = (0x40000000 + (_x) * DISCRETE_MAX_NODE_OUTPUTS), \
|
||||
#define NODE0_DEF(_x) NODE_ ## 0 ## _x = (0x40000000 + (_x) * DISCRETE_MAX_OUTPUTS), \
|
||||
NODE_ ## 0 ## _x ## _01, NODE_ ## 0 ## _x ## _02, NODE_ ## 0 ## _x ## _03, NODE_ ## 0 ## _x ## _04, \
|
||||
NODE_ ## 0 ## _x ## _05, NODE_ ## 0 ## _x ## _06, NODE_ ## 0 ## _x ## _07
|
||||
#define NODE_DEF(_x) NODE_ ## _x = (0x40000000 + (_x) * DISCRETE_MAX_NODE_OUTPUTS), \
|
||||
#define NODE_DEF(_x) NODE_ ## _x = (0x40000000 + (_x) * DISCRETE_MAX_OUTPUTS), \
|
||||
NODE_ ## _x ## _01, NODE_ ## _x ## _02, NODE_ ## _x ## _03, NODE_ ## _x ## _04, \
|
||||
NODE_ ## _x ## _05, NODE_ ## _x ## _06, NODE_ ## _x ## _07
|
||||
|
||||
@ -4066,15 +4066,15 @@ enum {
|
||||
|
||||
/* Some Pre-defined nodes for convenience */
|
||||
|
||||
#define NODE(_x) (NODE_00 + (_x) * DISCRETE_MAX_NODE_OUTPUTS)
|
||||
#define NODE(_x) (NODE_00 + (_x) * DISCRETE_MAX_OUTPUTS)
|
||||
#define NODE_SUB(_x, _y) (NODE(_x) + (_y))
|
||||
|
||||
#if DISCRETE_MAX_NODE_OUTPUTS == 8
|
||||
#if DISCRETE_MAX_OUTPUTS == 8
|
||||
#define NODE_CHILD_NODE_NUM(_x) ((int)(_x) & 7)
|
||||
#define NODE_DEFAULT_NODE(_x) ((int)(_x) & ~7)
|
||||
#define NODE_INDEX(_x) (((int)(_x) - NODE_START)>>3)
|
||||
#else
|
||||
#error "DISCRETE_MAX_NODE_OUTPUTS != 8"
|
||||
#error "DISCRETE_MAX_OUTPUTS != 8"
|
||||
#endif
|
||||
|
||||
#define NODE_RELATIVE(_x, _y) (NODE(NODE_INDEX(_x) + (_y)))
|
||||
|
Loading…
Reference in New Issue
Block a user