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:
Couriersud 2009-08-29 18:30:19 +00:00
parent 98a7eb3c8d
commit 68d94c232d
3 changed files with 120 additions and 87 deletions

View File

@ -31,6 +31,18 @@ struct dss_adjustment_context
double scale; 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) INLINE discrete_info *get_safe_token(const device_config *device)
{ {
assert(device != NULL); assert(device != NULL);
@ -50,14 +62,14 @@ READ8_DEVICE_HANDLER(discrete_sound_r)
/* Read the node input value if allowed */ /* Read the node input value if allowed */
if (node) 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 */ /* Bring the system up to now */
stream_update(info->discrete_stream); stream_update(info->discrete_stream);
if ((node->module->type >= DSS_INPUT_DATA) && (node->module->type <= DSS_INPUT_PULSE)) if ((node->module->type >= DSS_INPUT_DATA) && (node->module->type <= DSS_INPUT_PULSE))
{ {
data = *node_data; data = context->data;
} }
} }
else else
@ -75,8 +87,7 @@ WRITE8_DEVICE_HANDLER(discrete_sound_w)
/* Update the node input value if it's a proper input node */ /* Update the node input value if it's a proper input node */
if (node) if (node)
{ {
UINT8 *node_data = (UINT8 *)node->context; struct dss_input_context *context = (struct dss_input_context *)node->context;
UINT8 last_data = *node_data;
UINT8 new_data = 0; UINT8 new_data = 0;
switch (node->module->type) switch (node->module->type)
@ -93,15 +104,15 @@ WRITE8_DEVICE_HANDLER(discrete_sound_w)
break; break;
} }
if (last_data != new_data) if (context->data != new_data)
{ {
/* Bring the system up to now */ /* Bring the system up to now */
stream_update(info->discrete_stream); 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 */ /* 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 else
@ -217,33 +228,38 @@ static DISCRETE_RESET(dss_constant)
************************************************************************/ ************************************************************************/
static DISCRETE_RESET(dss_input) 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) switch (node->module->type)
{ {
case DSS_INPUT_DATA: case DSS_INPUT_DATA:
*node_data = DSS_INPUT__INIT; context->data = DSS_INPUT__INIT;
break; break;
case DSS_INPUT_LOGIC: case DSS_INPUT_LOGIC:
case DSS_INPUT_PULSE: case DSS_INPUT_PULSE:
*node_data = (DSS_INPUT__INIT == 0) ? 0 : 1; context->data = (DSS_INPUT__INIT == 0) ? 0 : 1;
break; break;
case DSS_INPUT_NOT: case DSS_INPUT_NOT:
*node_data = (DSS_INPUT__INIT == 0) ? 1 : 0; context->data = (DSS_INPUT__INIT == 0) ? 1 : 0;
break; 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) 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 */ /* Set a valid output */
node->output[0] = *node_data; node->output[0] = context->data;
/* Reset the input to default for the next cycle */ /* Reset the input to default for the next cycle */
/* node order is now important */ /* 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) static DISCRETE_STEP(dss_input_stream)
{ {
/* the context pointer is set to point to the current input stream data in discrete_stream_update */ /* 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; struct dss_input_context *context = (struct dss_input_context *)node->context;
stream_sample_t *data = *ptr;
if (data) if (context->ptr)
{ {
node->output[0] = (*data) * DSS_INPUT_STREAM__GAIN + DSS_INPUT_STREAM__OFFSET; node->output[0] = (*context->ptr) * context->gain + context->offset;
(*ptr)++; context->ptr++;
} }
else else
node->output[0] = 0; node->output[0] = 0;
@ -277,8 +292,13 @@ static DISCRETE_STEP(dss_input_stream)
static DISCRETE_RESET(dss_input_stream) static DISCRETE_RESET(dss_input_stream)
{ {
int istream = DSS_INPUT_STREAM__STREAM; struct dss_input_context *context = (struct dss_input_context *)node->context;
/* we will use the node's context pointer to point to the input stream data */
assert(istream < node->info->discrete_input_streams); assert(DSS_INPUT_STREAM__STREAM < linked_list_count(node->info->input_list));
node->context = (discrete_info *) &node->info->input_stream_data[istream]; 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;
} }

View File

@ -169,6 +169,39 @@ static DISCRETE_RESET( dso_output )
/* nothing to do - just avoid being stepped */ /* 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 * Included simulation objects
@ -191,7 +224,7 @@ static DISCRETE_RESET( dso_output )
static const discrete_module module_list[] = 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_CSVLOG ,"DSO_CSVLOG" , 0 ,0 ,NULL ,NULL },
{ DSO_WAVELOG ,"DSO_WAVELOG" , 0 ,0 ,NULL ,NULL }, { DSO_WAVELOG ,"DSO_WAVELOG" , 0 ,0 ,NULL ,NULL },
{ DSO_IMPORT ,"DSO_IMPORT" , 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 */ /* from disc_inp.c */
{ DSS_ADJUSTMENT ,"DSS_ADJUSTMENT" , 1 ,sizeof(struct dss_adjustment_context) ,dss_adjustment_reset ,dss_adjustment_step }, { 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_CONSTANT ,"DSS_CONSTANT" , 1 ,0 ,dss_constant_reset ,NULL },
{ DSS_INPUT_DATA ,"DSS_INPUT_DATA" , 1 ,sizeof(UINT8) ,dss_input_reset ,NULL }, { DSS_INPUT_DATA ,"DSS_INPUT_DATA" , 1 ,sizeof(struct dss_input_context) ,dss_input_reset ,NULL },
{ DSS_INPUT_LOGIC ,"DSS_INPUT_LOGIC" , 1 ,sizeof(UINT8) ,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(UINT8) ,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(UINT8) ,dss_input_reset ,dss_input_pulse_step }, { 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 ,0 ,dss_input_stream_reset,dss_input_stream_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 */ /* from disc_wav.c */
/* Generic modules */ /* Generic modules */
@ -304,20 +337,6 @@ static const discrete_module module_list[] =
{ DSS_NULL ,"DSS_NULL" , 0 ,0 ,NULL ,NULL } { 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 * Find a given node
@ -396,7 +415,7 @@ static void discrete_build_list(discrete_info *info, discrete_sound_block *intf,
else else
{ {
discrete_log(info, "discrete_build_list() - adding node %d (*current %p)\n", node_count, *current); 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++; node_count++;
@ -482,6 +501,8 @@ static DEVICE_START( discrete )
/* Start with empty lists */ /* Start with empty lists */
info->node_list = NULL; info->node_list = NULL;
info->step_list = NULL; info->step_list = NULL;
info->output_list = NULL;
info->input_list = NULL;
/* allocate memory to hold pointers to nodes by index */ /* allocate memory to hold pointers to nodes by index */
info->indexed_node = auto_alloc_array_clear(device->machine, node_description *, DISCRETE_MAX_NODES); 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 */ /* then set up the output nodes */
/* initialize the stream(s) */ /* 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 */ /* allocate a queue */
@ -749,22 +770,22 @@ static STREAM_UPDATE( discrete_stream_update )
{ {
discrete_info *info = (discrete_info *)param; discrete_info *info = (discrete_info *)param;
linked_list_entry *entry; linked_list_entry *entry;
int samplenum, nodenum, outputnum; int samplenum, outputnum;
//int j; int left; int run;
if (samples == 0) if (samples == 0)
return; return;
/* Setup any output streams */ /* 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 */ /* 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) 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) 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 **cur_task_node = NULL;
linked_list_entry *entry; linked_list_entry *entry;
discrete_task_context *task = NULL; discrete_task_context *task = NULL;
/* list tail pointers */
/* start with no outputs or input streams */ linked_list_entry **step_list = &info->step_list;
linked_list_entry **node_list = &info->node_list;
info->discrete_outputs = 0; linked_list_entry **task_list = &info->task_list;
info->discrete_input_streams = 0; linked_list_entry **output_list = &info->output_list;
linked_list_entry **input_list = &info->input_list;
/* loop over all nodes */ /* loop over all nodes */
for (entry = block_list; entry != NULL; entry = entry->next) 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 */ /* Output Node */
case DSO_OUTPUT: case DSO_OUTPUT:
if (info->discrete_outputs == DISCRETE_MAX_OUTPUTS) linked_list_add(info, &output_list, node);
fatalerror("init_nodes() - There can not be more then %d output nodes", DISCRETE_MAX_OUTPUTS);
info->output_node[info->discrete_outputs++] = node;
break; break;
/* CSVlog Node for debugging */ /* 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) if (cur_task_node != NULL)
fatalerror("init_nodes() - Nested DISCRETE_START_TASK."); fatalerror("init_nodes() - Nested DISCRETE_START_TASK.");
task = auto_alloc_clear(info->device->machine, discrete_task_context); 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; cur_task_node = &task->list;
break; 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 we are an stream input node, track that */
if (block->type == DSS_INPUT_STREAM) if (block->type == DSS_INPUT_STREAM)
{ {
if (info->discrete_input_streams == DISCRETE_MAX_OUTPUTS) linked_list_add(info, &input_list, node);
fatalerror("init_nodes() - There can not be more then %d input stream nodes", DISCRETE_MAX_OUTPUTS);
node->context = NULL;
info->discrete_input_streams++;
} }
/* add to node list */ /* 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 */ /* our running order just follows the order specified */
/* does the node step ? */ /* 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? */ /* do we belong to a task? */
if (cur_task_node == NULL) if (cur_task_node == NULL)
add_list(info, &step_list, node); linked_list_add(info, &step_list, node);
else else
add_list(info, &cur_task_node, node); linked_list_add(info, &cur_task_node, node);
} }
if (block->type == DSO_TASK_END) 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 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"); fatalerror("init_nodes() - Couldn't find an output node");
} }

View File

@ -438,8 +438,12 @@
* Note: The discrete system is floating point based. So when routing a stream * 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 * set it's gain to 100% and then use DISCRETE_INPUTX_STREAM to adjust
* it if needed. * 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_NODES 300
#define DISCRETE_MAX_INPUTS 10 #define DISCRETE_MAX_INPUTS 10
#define DISCRETE_MAX_OUTPUTS 16
#define DISCRETE_MAX_WAVELOGS 10 #define DISCRETE_MAX_WAVELOGS 10
#define DISCRETE_MAX_CSVLOGS 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 struct _node_description
{ {
int node; /* The node's index number in the node list */ 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 active_inputs; /* Number of active inputs on this node type */
int input_is_node; /* Bit Flags. 1 in bit location means input_is_node */ 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 *ptr;
double node_buf[2048]; double node_buf[2048];
double **dest; double **dest;
volatile INT32 active;
}; };
struct _discrete_info struct _discrete_info
@ -3695,12 +3697,10 @@ struct _discrete_info
linked_list_entry *task_list; /* discrete_task_context * */ linked_list_entry *task_list; /* discrete_task_context * */
/* the input streams */ /* the input streams */
int discrete_input_streams; linked_list_entry *input_list;
stream_sample_t *input_stream_data[DISCRETE_MAX_OUTPUTS];
/* output node tracking */ /* output node tracking */
int discrete_outputs; linked_list_entry *output_list;
node_description *output_node[DISCRETE_MAX_OUTPUTS];
/* the output stream */ /* the output stream */
sound_stream *discrete_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 ## _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 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 ## _01, NODE_ ## _x ## _02, NODE_ ## _x ## _03, NODE_ ## _x ## _04, \
NODE_ ## _x ## _05, NODE_ ## _x ## _06, NODE_ ## _x ## _07 NODE_ ## _x ## _05, NODE_ ## _x ## _06, NODE_ ## _x ## _07
@ -4066,15 +4066,15 @@ enum {
/* Some Pre-defined nodes for convenience */ /* 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)) #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_CHILD_NODE_NUM(_x) ((int)(_x) & 7)
#define NODE_DEFAULT_NODE(_x) ((int)(_x) & ~7) #define NODE_DEFAULT_NODE(_x) ((int)(_x) & ~7)
#define NODE_INDEX(_x) (((int)(_x) - NODE_START)>>3) #define NODE_INDEX(_x) (((int)(_x) - NODE_START)>>3)
#else #else
#error "DISCRETE_MAX_NODE_OUTPUTS != 8" #error "DISCRETE_MAX_OUTPUTS != 8"
#endif #endif
#define NODE_RELATIVE(_x, _y) (NODE(NODE_INDEX(_x) + (_y))) #define NODE_RELATIVE(_x, _y) (NODE(NODE_INDEX(_x) + (_y)))