/*
    ZEsarUX  ZX Second-Emulator And Released for UniX
    Copyright (C) 2013 Cesar Hernandez Bano

    This file is part of ZEsarUX.

    ZEsarUX is free 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.

    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/>.

*/

/*
   Menu entries
*/

//
// Archivo para entradas de menu, excluyendo funciones auxiliares de soporte de menu, y tampoco los menus de settings
// Las funciones auxiliares de menu estan en menu.c
//


#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <time.h>
#include <stdarg.h>
#include <dirent.h>

#include <sys/types.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <errno.h>


#include "zxvision.h"
#include "menu_items.h"
#include "menu_items_storage.h"
#include "menu_items_settings.h"
#include "menu_debug_cpu.h"
#include "menu_file_viewer_browser.h"
#include "menu_filesel.h"
#include "menu_zeng_online.h"
#include "menu_bitmaps.h"
#include "screen.h"
#include "cpu.h"
#include "start.h"
#include "debug.h"
#include "zx8081.h"
#include "ay38912.h"
#include "tape.h"
#include "audio.h"
#include "audio_ayplayer.h"
#include "timer.h"
#include "snap.h"
#include "operaciones.h"
#include "disassemble.h"
#include "utils.h"
#include "contend.h"
#include "joystick.h"
#include "ula.h"
#include "printers.h"
#include "realjoystick.h"
#include "scrstdout.h"
#include "z88.h"
#include "ulaplus.h"
#include "autoselectoptions.h"
#include "zxuno.h"
#include "charset.h"
#include "chardetect.h"
#include "textspeech.h"
#include "mmc.h"
#include "ide.h"
#include "divmmc.h"
#include "divide.h"
#include "diviface.h"
#include "zxpand.h"
#include "spectra.h"
#include "spritechip.h"
#include "jupiterace.h"
#include "timex.h"
#include "chloe.h"
#include "prism.h"
#include "cpc.h"
#include "sam.h"
#include "atomlite.h"
#include "if1.h"
#include "pd765.h"
#include "tbblue.h"
#include "dandanator.h"
#include "superupgrade.h"
#include "m68k.h"
#include "remote.h"
#include "snap_rzx.h"
#include "multiface.h"
#include "scmp.h"
#include "esxdos_handler.h"
#include "tsconf.h"
#include "spritefinder.h"
#include "snap_spg.h"
#include "betadisk.h"
#include "tape_tzx.h"
#include "snap_zsf.h"
#include "compileoptions.h"
#include "settings.h"
#include "datagear.h"
#include "assemble.h"
#include "expression_parser.h"
#include "uartbridge.h"
#include "zeng.h"
#include "network.h"
#include "stats.h"
#include "vdp_9918a.h"
#include "msx.h"
#include "coleco.h"
#include "sg1000.h"
#include "sms.h"
#include "sn76489an.h"
#include "svi.h"
#include "ql_qdos_handler.h"
#include "ql_i8049.h"
#include "gs.h"
#include "zvfs.h"
#include "vdp_9918a_sms.h"
#include "snap_ram.h"
#include "sensors.h"
#include "samram.h"
#include "utils_text_adventure.h"
#include "about_logo.h"
#include "transtape.h"
#include "mhpokeador.h"
#include "specmate.h"
#include "phoenix.h"
#include "defcon.h"
#include "ramjet.h"
#include "interface007.h"
#include "dinamid3.h"
#include "dsk.h"
#include "plus3dos_handler.h"
#include "pcw.h"
#include "zeng_online.h"
#include "zeng_online_client.h"
#include "mk14.h"
#include "microdrive.h"
#include "microdrive_raw.h"
#include "lec.h"
#include "zxmmcplus.h"

#ifdef COMPILE_ALSA
#include "audioalsa.h"
#endif


#ifdef COMPILE_ONEBITSPEAKER
#include "audioonebitspeaker.h"
#endif


#if defined(__APPLE__)
	#include <sys/syslimits.h>

	#include <sys/resource.h>

#endif


#ifdef COMPILE_CURSES
	#include "scrcurses.h"
#endif

#ifdef COMPILE_AA
	#include "scraa.h"
#endif

#ifdef COMPILE_STDOUT
	#include "scrstdout.h"
#endif


#ifdef COMPILE_XWINDOWS
	#include "scrxwindows.h"
#endif

#ifdef COMPILE_CURSESW
	#include "cursesw_ext.h"
#endif


#ifdef COMPILE_SDL
#ifdef COMPILE_SDL2

    #include "audiosdl2.h"

#endif
#endif


//Opciones seleccionadas para cada menu
int debug_pok_file_opcion_seleccionada=0;
int poke_opcion_seleccionada=0;
int audio_new_waveform_opcion_seleccionada=0;
int debug_new_visualmem_opcion_seleccionada=0;
int audio_new_ayplayer_opcion_seleccionada=0;
int osd_adventure_keyboard_opcion_seleccionada=0;
int tsconf_layer_settings_opcion_seleccionada=0;
int cpu_stats_opcion_seleccionada=0;
int cpu_transaction_log_opcion_seleccionada=0;
int watches_opcion_seleccionada=0;
int record_mid_opcion_seleccionada=0;
int record_mid_instrument_opcion_seleccionada=0;
int direct_midi_output_opcion_seleccionada=0;
int ay_mixer_opcion_seleccionada=0;
int uartbridge_opcion_seleccionada=0;
int network_opcion_seleccionada=0;
int zeng_opcion_seleccionada=0;
int zx81_online_browser_opcion_seleccionada=0;
int online_browse_zx81_letter_opcion_seleccionada=0;
int mmc_divmmc_opcion_seleccionada=0;
int ide_divide_opcion_seleccionada=0;
int display_settings_opcion_seleccionada=0;
int debug_tsconf_opcion_seleccionada=0;
int windows_opcion_seleccionada=0;
int ql_mdv_flp_opcion_seleccionada=0;
int i8049_mixer_opcion_seleccionada=0;
int midi_output_instrument_opcion_seleccionada=0;
int snapshot_rewind_opcion_seleccionada=0;
int find_opcion_seleccionada=0;
int find_bytes_opcion_seleccionada=0;
int find_lives_opcion_seleccionada=0;
int audio_visual_realtape_opcion_seleccionada=0;
int hotswap_machine_opcion_seleccionada=0;
int custom_machine_opcion_seleccionada=0;
int machine_selection_por_fabricante_opcion_seleccionada=0;
int machine_selection_opcion_seleccionada=0;
int menu_machine_selection_by_name_opcion_seleccionada=0;
int licenses_opcion_seleccionada=0;
int about_opcion_seleccionada=0;
int input_file_keyboard_opcion_seleccionada=0;
int audio_opcion_seleccionada=0;
int debug_opcion_seleccionada=0;
int snapshot_opcion_seleccionada=0;


int msxcart_opcion_seleccionada=0;
int z88_eprom_size_opcion_seleccionada=0;
int z88_flash_intel_size_opcion_seleccionada=0;

int storage_opcion_seleccionada=0;

int zxuno_spi_flash_opcion_seleccionada=0;

int menu_inicio_opcion_seleccionada=0;
int zxdesktop_trash_opcion_seleccionada=0;
int storage_copy_devices_opcion_seleccionada=0;
int multiface_opcion_seleccionada=0;

int transtape_opcion_seleccionada=0;
int mhpokeador_opcion_seleccionada=0;
int specmate_opcion_seleccionada=0;
int phoenix_opcion_seleccionada=0;
int defcon_opcion_seleccionada=0;
int ramjet_opcion_seleccionada=0;
int interface007_opcion_seleccionada=0;
int dinamid3_opcion_seleccionada=0;
int mantransfe_opcion_seleccionada=0;


int menu_display_window_list_opcion_seleccionada=0;
int toys_opcion_seleccionada=0;
int zxlife_opcion_seleccionada=0;
int text_adventure_tools_opcion_seleccionada=0;
int in_memoriam_opcion_seleccionada=0;
int in_memoriam_david_opcion_seleccionada=0;
int in_memoriam_diego_opcion_seleccionada=0;
int memory_cheat_opcion_seleccionada=0;
int menu_memory_cheat_first_scan_opcion_seleccionada=0;
int menu_memory_cheat_next_scan_opcion_seleccionada=0;
int cpc_additional_roms_opcion_seleccionada=0;
int lec_memory_opcion_seleccionada=0;
int visualcasette_tape_opcion_seleccionada=0;
int debug_view_basic_opcion_seleccionada=0;
int external_audio_source_to_disk_opcion_seleccionada;

//int mdv_simulate_bad_sectors_opcion_seleccionada=0;

//Fin opciones seleccionadas para cada menu

//Variable solo usada como template
int template_menu_opcion_seleccionada;

//Ultima direccion pokeada
int last_debug_poke_dir=16384;



char last_ay_file[PATH_MAX]="";




char last_msx_cart[PATH_MAX]="";





int menu_tape_settings_cond(void)
{
	return !(MACHINE_IS_Z88);
}

char *menu_debug_poke_address_historial[UTIL_SCANF_HISTORY_MAX_LINES]={
    NULL
};

char *menu_debug_poke_value_historial[UTIL_SCANF_HISTORY_MAX_LINES]={
    NULL
};

void menu_debug_poke(MENU_ITEM_PARAMETERS)
{

	int veces;
	menu_z80_moto_int dir,valor_poke;

    char string_address[10];

	string_address[0]=0;

    //menu_ventana_scanf("Value?",string_address,10);
    int tecla=zxvision_scanf_history("Address?",string_address,10,menu_debug_poke_address_historial);

	if (tecla==2) return; //sale con ESC


	//Evaluar la dirección como una expresión, así podemos usar registros, sumas, etc
	int result=menu_debug_cpu_calculate_expression(string_address,&dir);

	if (result!=0) return; //Error parseando





    char string_value[10];

	string_value[0]=0;

    //menu_ventana_scanf("Value?",string_address,10);
    tecla=zxvision_scanf_history("Poke value?",string_value,10,menu_debug_poke_value_historial);

	if (tecla==2) return; //sale con ESC


	//Evaluar la dirección como una expresión, así podemos usar registros, sumas, etc
	result=menu_debug_cpu_calculate_expression(string_value,&valor_poke);

	if (result!=0) return; //Error parseando


	if (valor_poke<0 || valor_poke>255) {
			debug_printf (VERBOSE_ERR,"Invalid value %d",valor_poke);
			return;
	}

	char string_veces[6];
	sprintf (string_veces,"1");

	menu_ventana_scanf("How many bytes?",string_veces,6);

	veces=parse_string_to_number(string_veces);

	if (veces<1 || veces>65536) {
                debug_printf (VERBOSE_ERR,"Invalid quantity %d",veces);
		return;
	}


	for (;veces;veces--,dir++) {
		menu_debug_write_mapped_byte(dir,valor_poke);

	}

}



void old_menu_debug_poke(MENU_ITEM_PARAMETERS)
{

        int valor_poke,dir,veces;

        char string_poke[4];
        char string_dir[10];
	char string_veces[6];

        sprintf (string_dir,"%XH",last_debug_poke_dir);

        menu_ventana_scanf("Address",string_dir,10);

        dir=parse_string_to_number(string_dir);

        /*if ( (dir<0 || dir>65535) && MACHINE_IS_SPECTRUM) {
                debug_printf (VERBOSE_ERR,"Invalid address %d",dir);
                return;
        }*/

				last_debug_poke_dir=dir;

        sprintf (string_poke,"0");

        menu_ventana_scanf("Poke Value",string_poke,4);

        valor_poke=parse_string_to_number(string_poke);

        if (valor_poke<0 || valor_poke>255) {
                debug_printf (VERBOSE_ERR,"Invalid value %d",valor_poke);
                return;
        }


	sprintf (string_veces,"1");

	menu_ventana_scanf("How many bytes?",string_veces,6);

	veces=parse_string_to_number(string_veces);

	if (veces<1 || veces>65536) {
                debug_printf (VERBOSE_ERR,"Invalid quantity %d",veces);
		return;
	}


	for (;veces;veces--,dir++) {

	        //poke_byte_no_time(dir,valor_poke);
		//poke_byte_z80_moto(dir,valor_poke);
		menu_debug_write_mapped_byte(dir,valor_poke);

	}

}


void menu_debug_poke_pok_file_select(char *pokfile)
{

        //contenido
        //MAX_LINEAS_POK_FILE es maximo de lineas de pok file
        //normalmente la tabla de pokes sera menor que el numero de lineas en el archivo .pok
        //struct s_pokfile tabla_pokes[MAX_LINEAS_POK_FILE];
        struct s_pokfile *tabla_pokes;
        tabla_pokes=malloc(sizeof(struct s_pokfile)*MAX_LINEAS_POK_FILE);

        //punteros
        //struct s_pokfile *punteros_pokes[MAX_LINEAS_POK_FILE];
        struct s_pokfile **punteros_pokes;
        punteros_pokes=malloc(sizeof(struct s_pokfile *)*MAX_LINEAS_POK_FILE);


        if (tabla_pokes==NULL || punteros_pokes==NULL) cpu_panic("Can not allocate memory for poke table");

        int i;
        for (i=0;i<MAX_LINEAS_POK_FILE;i++) punteros_pokes[i]=&tabla_pokes[i];





		int total=util_parse_pok_file(pokfile,punteros_pokes);

		if (total<1) {
			debug_printf (VERBOSE_ERR,"Error parsing POK file");
			return;
		}

		int j;
		for (j=0;j<total;j++) {
			debug_printf (VERBOSE_DEBUG,"menu poke index %d text %s bank %d address %d value %d value_orig %d",
				punteros_pokes[j]->indice_accion,
				punteros_pokes[j]->texto,
				punteros_pokes[j]->banco,
				punteros_pokes[j]->direccion,
				punteros_pokes[j]->valor,
				punteros_pokes[j]->valor_orig);
		}


		//Meter cada poke en un menu




        menu_item *array_menu_debug_pok_file;
        menu_item item_seleccionado;
        int retorno_menu;
        //Resetear siempre ultima linea = 0
        debug_pok_file_opcion_seleccionada=0;

        //temporal para mostrar todos los caracteres 0-255
        //int temp_conta=1;

        do {


            //Meter primer item de menu
            //truncar texto a 28 caracteres si excede de eso
            if (strlen(punteros_pokes[0]->texto)>28) punteros_pokes[0]->texto[28]=0;

            menu_add_item_menu_inicial_format(&array_menu_debug_pok_file,MENU_OPCION_NORMAL,NULL,NULL,"%s", punteros_pokes[0]->texto);


            //Luego recorrer array de pokes y cuando el numero de poke se incrementa, agregar
            int poke_anterior=0;

            int total_elementos=1;

            for (j=1;j<total;j++) {
                if (punteros_pokes[j]->indice_accion!=poke_anterior) {

                    //temp para mostrar todos los caracteres 0-255
                    //int kk;
                    //for (kk=0;kk<strlen(punteros_pokes[j]->texto);kk++) {
                    //	punteros_pokes[j]->texto[kk]=temp_conta++;
                    //	if (temp_conta==256) temp_conta=1;
                    //}

                    poke_anterior=punteros_pokes[j]->indice_accion;
                    //truncar texto a 28 caracteres si excede de eso
                    if (strlen(punteros_pokes[j]->texto)>28) punteros_pokes[j]->texto[28]=0;
                    menu_add_item_menu_format(array_menu_debug_pok_file,MENU_OPCION_NORMAL,NULL,NULL,"%s", punteros_pokes[j]->texto);

                    total_elementos++;
                    if (total_elementos==20) {
                        debug_printf (VERBOSE_DEBUG,"Too many pokes to show on Window. Showing only first 20");
                        menu_warn_message("Too many pokes to show on Window. Showing only first 20");
                        break;
                    }


                }
            }



            menu_add_item_menu(array_menu_debug_pok_file,"",MENU_OPCION_SEPARADOR,NULL,NULL);


            //menu_add_item_menu(array_menu_debug_pok_file,"ESC Back",MENU_OPCION_NORMAL|MENU_OPCION_ESC,NULL,NULL);
            menu_add_ESC_item(array_menu_debug_pok_file);

            retorno_menu=menu_dibuja_menu_dialogo_no_title_lang(&debug_pok_file_opcion_seleccionada,&item_seleccionado,array_menu_debug_pok_file,"Select Poke" );



            if ((item_seleccionado.tipo_opcion&MENU_OPCION_ESC)==0 && retorno_menu>=0) {
                            //llamamos por valor de funcion

                //Hacer poke sabiendo la linea seleccionada. Desde ahi, ejecutar todos los pokes de dicha accion
                debug_printf (VERBOSE_DEBUG,"Doing poke/s from line %d",debug_pok_file_opcion_seleccionada);

                z80_byte banco;
                z80_int direccion;
                z80_byte valor;

                //buscar indice_accion
                int result_poke=0;
                for (j=0;j<total && result_poke==0;j++) {

                    debug_printf (VERBOSE_DEBUG,"index %d looking %d current %d",j,debug_pok_file_opcion_seleccionada,punteros_pokes[j]->indice_accion);

                    if (punteros_pokes[j]->indice_accion==debug_pok_file_opcion_seleccionada) {
                        banco=punteros_pokes[j]->banco;
                        direccion=punteros_pokes[j]->direccion;
                        valor=punteros_pokes[j]->valor;
                        debug_printf (VERBOSE_DEBUG,"Doing poke bank %d address %d value %d",banco,direccion,valor);
                        result_poke=util_poke(banco,direccion,valor);
                    }




                }

                if (result_poke==0) menu_generic_message("Poke","OK. Poke applied");
            }

        } while ( (item_seleccionado.tipo_opcion&MENU_OPCION_ESC)==0 && retorno_menu!=MENU_RETORNO_ESC && !salir_todos_menus);


        free(tabla_pokes);
        free(punteros_pokes);




}


void menu_debug_poke_pok_file(MENU_ITEM_PARAMETERS)
{

    char *filtros[2];

    filtros[0]="pok";
    filtros[1]=0;

	char pokfile[PATH_MAX];

    int ret;

    ret=menu_filesel("Select POK File",filtros,pokfile);

    if (ret==1) {
        menu_debug_poke_pok_file_select(pokfile);

    }


}





void menu_poke(MENU_ITEM_PARAMETERS)
{
        menu_item *array_menu_poke;
        menu_item item_seleccionado;
        int retorno_menu;

        do {


                menu_add_item_menu_inicial_format(&array_menu_poke,MENU_OPCION_NORMAL,menu_debug_poke,NULL,"~~Poke");
                menu_add_item_menu_add_flags(array_menu_poke,MENU_ITEM_FLAG_GENERA_VENTANA | MENU_ITEM_FLAG_SE_CERRARA);
                menu_add_item_menu_shortcut(array_menu_poke,'p');
                menu_add_item_menu_es_sencillo(array_menu_poke);
                menu_add_item_menu_tooltip(array_menu_poke,"Poke address");
                menu_add_item_menu_ayuda(array_menu_poke,"Poke address for infinite lives, etc... This item follows active memory zone. "
					"You can also poke on read-only memory, depending on the current memory zone");

		//No tiene sentido pues se puede usar las memory zones para esto
		/*if (MACHINE_IS_SPECTRUM_128_P2_P2A_P3 || MACHINE_IS_ZXUNO_BOOTM_DISABLED) {
			menu_add_item_menu(array_menu_poke,"Poke 128~~k mode",MENU_OPCION_NORMAL,menu_debug_poke_128k,NULL);
			menu_add_item_menu_shortcut(array_menu_poke,'k');
			menu_add_item_menu_tooltip(array_menu_poke,"Poke bank & address");
			menu_add_item_menu_ayuda(array_menu_poke,"Poke bank & address");
		}*/

		if (MACHINE_IS_SPECTRUM) {
			menu_add_item_menu(array_menu_poke,"Poke from .POK ~~file",MENU_OPCION_NORMAL,menu_debug_poke_pok_file,NULL);
            menu_add_item_menu_add_flags(array_menu_poke,MENU_ITEM_FLAG_GENERA_VENTANA | MENU_ITEM_FLAG_SE_CERRARA);
			menu_add_item_menu_shortcut(array_menu_poke,'f');
            menu_add_item_menu_es_sencillo(array_menu_poke);
			menu_add_item_menu_tooltip(array_menu_poke,"Poke reading .POK file");
			menu_add_item_menu_ayuda(array_menu_poke,"Poke reading .POK file");
		}


                menu_add_item_menu(array_menu_poke,"",MENU_OPCION_SEPARADOR,NULL,NULL);


                //menu_add_item_menu(array_menu_poke,"ESC Back",MENU_OPCION_NORMAL|MENU_OPCION_ESC,NULL,NULL);
                menu_add_ESC_item(array_menu_poke);

                retorno_menu=menu_dibuja_menu(&poke_opcion_seleccionada,&item_seleccionado,array_menu_poke,
                    "Poke Menu","Menú Poke","Menú Poke" );



                if ((item_seleccionado.tipo_opcion&MENU_OPCION_ESC)==0 && retorno_menu>=0) {
                        //llamamos por valor de funcion
                        if (item_seleccionado.menu_funcion!=NULL) {
                                //printf ("actuamos por funcion\n");
                                item_seleccionado.menu_funcion(item_seleccionado.valor_opcion);

                        }
                }

        } while ( (item_seleccionado.tipo_opcion&MENU_OPCION_ESC)==0 && retorno_menu!=MENU_RETORNO_ESC && !salir_todos_menus);
}


#ifdef EMULATE_CPU_STATS

void menu_debug_cpu_stats_clear_disassemble_array(void)
{
	int i;

	for (i=0;i<DISASSEMBLE_ARRAY_LENGTH;i++) disassemble_array[i]=0;
}

void menu_debug_cpu_stats_diss_no_print(z80_byte index,z80_byte opcode,char *dumpassembler)
{

        size_t longitud_opcode;

        disassemble_array[index]=opcode;

        debugger_disassemble_array(dumpassembler,31,&longitud_opcode,0);
}


/*
void menu_debug_cpu_stats_diss(z80_byte index,z80_byte opcode,int linea)
{

	char dumpassembler[32];

	//Empezar con espacio
	dumpassembler[0]=' ';

	menu_debug_cpu_stats_diss_no_print(index,opcode,&dumpassembler[1]);

	menu_escribe_linea_opcion(linea,-1,1,dumpassembler);
}
*/

void menu_debug_cpu_stats_diss_complete_no_print (z80_byte opcode,char *buffer,z80_byte preffix1,z80_byte preffix2)
{

	//Sin prefijo
	if (preffix1==0) {
                        menu_debug_cpu_stats_clear_disassemble_array();
                        menu_debug_cpu_stats_diss_no_print(0,opcode,buffer);
	}

	//Con 1 solo prefijo
	else if (preffix2==0) {
                        menu_debug_cpu_stats_clear_disassemble_array();
                        disassemble_array[0]=preffix1;
                        menu_debug_cpu_stats_diss_no_print(1,opcode,buffer);
	}

	//Con 2 prefijos (DD/FD + CB)
	else {
                        //Opcode
                        menu_debug_cpu_stats_clear_disassemble_array();
                        disassemble_array[0]=preffix1;
                        disassemble_array[1]=preffix2;
                        disassemble_array[2]=0;
                        menu_debug_cpu_stats_diss_no_print(3,opcode,buffer);
	}
}



void menu_cpu_full_stats(unsigned int *stats_table,char *title,z80_byte preffix1,z80_byte preffix2)
{

	int index_op,index_buffer;
	unsigned int counter;

    char *stats_buffer=util_malloc_max_texto_generic_message("Can not allocate memory for showing stats");

	char dumpassembler[32];

	//margen suficiente para que quepa una linea y un contador int de 32 bits...
	//aunque si pasa el ancho de linea, la rutina de generic_message lo troceara
	char buf_linea[64];

	index_buffer=0;

	for (index_op=0;index_op<256;index_op++) {
		counter=util_stats_get_counter(stats_table,index_op);

		menu_debug_cpu_stats_diss_complete_no_print(index_op,dumpassembler,preffix1,preffix2);

		sprintf (buf_linea,"%02X %-16s: %u \n",index_op,dumpassembler,counter);
		//16 ocupa la instruccion mas larga: LD B,RLC (IX+dd)

		sprintf (&stats_buffer[index_buffer],"%s\n",buf_linea);
		//sprintf (&stats_buffer[index_buffer],"%02X: %11d\n",index_op,counter);
		//index_buffer +=16;
		index_buffer +=strlen(buf_linea);
	}

	stats_buffer[index_buffer]=0;

	menu_generic_message(title,stats_buffer);

    free(stats_buffer);

}

void menu_cpu_full_stats_codsinpr(MENU_ITEM_PARAMETERS)
{
	menu_cpu_full_stats(stats_codsinpr,"Full statistic no preffix",0,0);
}

void menu_cpu_full_stats_codpred(MENU_ITEM_PARAMETERS)
{
        menu_cpu_full_stats(stats_codpred,"Full statistic pref ED",0xED,0);
}

void menu_cpu_full_stats_codprcb(MENU_ITEM_PARAMETERS)
{
        menu_cpu_full_stats(stats_codprcb,"Full statistic pref CB",0xCB,0);
}

void menu_cpu_full_stats_codprdd(MENU_ITEM_PARAMETERS)
{
        menu_cpu_full_stats(stats_codprdd,"Full statistic pref DD",0xDD,0);
}

void menu_cpu_full_stats_codprfd(MENU_ITEM_PARAMETERS)
{
        menu_cpu_full_stats(stats_codprfd,"Full statistic pref FD",0xFD,0);
}

void menu_cpu_full_stats_codprddcb(MENU_ITEM_PARAMETERS)
{
        menu_cpu_full_stats(stats_codprddcb,"Full statistic pref DDCB",0xDD,0xCB);
}

void menu_cpu_full_stats_codprfdcb(MENU_ITEM_PARAMETERS)
{
        menu_cpu_full_stats(stats_codprfdcb,"Full statistic pref FDCB",0xFD,0xCB);
}


void menu_cpu_full_stats_clear(MENU_ITEM_PARAMETERS)
{
	util_stats_init();

	menu_generic_message_splash("Clear CPU statistics","OK. Statistics cleared");
}


void menu_debug_cpu_stats(MENU_ITEM_PARAMETERS)
{
    menu_item *array_menu_cpu_stats;
    menu_item item_seleccionado;
    int retorno_menu;
    do {
        menu_add_item_menu_inicial_format(&array_menu_cpu_stats,MENU_OPCION_NORMAL,menu_debug_cpu_resumen_stats,NULL,"Compact Statistics");
        menu_add_item_menu_genera_ventana(array_menu_cpu_stats);
        menu_add_item_menu_se_cerrara(array_menu_cpu_stats);
        menu_add_item_menu_tooltip(array_menu_cpu_stats,"Shows Compact CPU Statistics");
        menu_add_item_menu_ayuda(array_menu_cpu_stats,"Shows the most used opcode for every type: without preffix, with ED preffix, "
            "etc. CPU Statistics are reset when changing machine or resetting CPU.");


        menu_add_item_menu_format(array_menu_cpu_stats,MENU_OPCION_NORMAL,menu_cpu_full_stats_codsinpr,NULL,"Full Statistics No pref");
        menu_add_item_menu_add_flags(array_menu_cpu_stats,MENU_ITEM_FLAG_GENERA_VENTANA | MENU_ITEM_FLAG_SE_CERRARA);

        menu_add_item_menu_format(array_menu_cpu_stats,MENU_OPCION_NORMAL,menu_cpu_full_stats_codpred,NULL,"Full Statistics Pref ED");
        menu_add_item_menu_add_flags(array_menu_cpu_stats,MENU_ITEM_FLAG_GENERA_VENTANA | MENU_ITEM_FLAG_SE_CERRARA);

        menu_add_item_menu_format(array_menu_cpu_stats,MENU_OPCION_NORMAL,menu_cpu_full_stats_codprcb,NULL,"Full Statistics Pref CB");
        menu_add_item_menu_add_flags(array_menu_cpu_stats,MENU_ITEM_FLAG_GENERA_VENTANA | MENU_ITEM_FLAG_SE_CERRARA);

        menu_add_item_menu_format(array_menu_cpu_stats,MENU_OPCION_NORMAL,menu_cpu_full_stats_codprdd,NULL,"Full Statistics Pref DD");
        menu_add_item_menu_add_flags(array_menu_cpu_stats,MENU_ITEM_FLAG_GENERA_VENTANA | MENU_ITEM_FLAG_SE_CERRARA);

        menu_add_item_menu_format(array_menu_cpu_stats,MENU_OPCION_NORMAL,menu_cpu_full_stats_codprfd,NULL,"Full Statistics Pref FD");
        menu_add_item_menu_add_flags(array_menu_cpu_stats,MENU_ITEM_FLAG_GENERA_VENTANA | MENU_ITEM_FLAG_SE_CERRARA);

        menu_add_item_menu_format(array_menu_cpu_stats,MENU_OPCION_NORMAL,menu_cpu_full_stats_codprddcb,NULL,"Full Statistics Pref DDCB");
        menu_add_item_menu_add_flags(array_menu_cpu_stats,MENU_ITEM_FLAG_GENERA_VENTANA | MENU_ITEM_FLAG_SE_CERRARA);

        menu_add_item_menu_format(array_menu_cpu_stats,MENU_OPCION_NORMAL,menu_cpu_full_stats_codprfdcb,NULL,"Full Statistics Pref FDCB");
        menu_add_item_menu_add_flags(array_menu_cpu_stats,MENU_ITEM_FLAG_GENERA_VENTANA | MENU_ITEM_FLAG_SE_CERRARA);

        menu_add_item_menu_format(array_menu_cpu_stats,MENU_OPCION_NORMAL,menu_cpu_full_stats_clear,NULL,"Clear Statistics");
        menu_add_item_menu_add_flags(array_menu_cpu_stats,MENU_ITEM_FLAG_SE_CERRARA);


        menu_add_item_menu(array_menu_cpu_stats,"",MENU_OPCION_SEPARADOR,NULL,NULL);
        //menu_add_item_menu(array_menu_cpu_stats,"ESC Back",MENU_OPCION_NORMAL|MENU_OPCION_ESC,NULL,NULL);
        menu_add_ESC_item(array_menu_cpu_stats);

        retorno_menu=menu_dibuja_menu(&cpu_stats_opcion_seleccionada,&item_seleccionado,array_menu_cpu_stats,
            "CPU Statistics Menu","Menú Estadísticas CPU","Menú Estadístiques CPU" );


        if ((item_seleccionado.tipo_opcion&MENU_OPCION_ESC)==0 && retorno_menu>=0) {
            //llamamos por valor de funcion
            if (item_seleccionado.menu_funcion!=NULL) {
                    //printf ("actuamos por funcion\n");
                    item_seleccionado.menu_funcion(item_seleccionado.valor_opcion);

            }
        }

    } while ( (item_seleccionado.tipo_opcion&MENU_OPCION_ESC)==0 && retorno_menu!=MENU_RETORNO_ESC && !salir_todos_menus);
}

int cpu_stats_valor_contador_segundo_anterior;

zxvision_window *menu_debug_cpu_resumen_stats_overlay_window;

void menu_debug_cpu_resumen_stats_overlay(void)
{



    //si ventana minimizada, no ejecutar todo el codigo de overlay
    if (menu_debug_cpu_resumen_stats_overlay_window->is_minimized) return;

    //printf("Overlay cpu stats %d\n",contador_segundo);

    char textostats[32];
	zxvision_window *ventana;

	ventana=menu_debug_cpu_resumen_stats_overlay_window;


    char dumpassembler[32];

    //Empezar con espacio
    dumpassembler[0]=' ';



    //printf ("%d %d\n",contador_segundo,cpu_stats_valor_contador_segundo_anterior);


    //esto hara ejecutar esto 2 veces por segundo
    if ( ((contador_segundo%500) == 0 && cpu_stats_valor_contador_segundo_anterior!=contador_segundo) || menu_multitarea==0) {
											cpu_stats_valor_contador_segundo_anterior=contador_segundo;
        //printf ("Refrescando. contador_segundo=%d\n",contador_segundo);

        int linea=0;
        int opcode;

        unsigned int sumatotal;
        sumatotal=util_stats_sum_all_counters();
        sprintf (textostats,"Total opcodes run: %u",sumatotal);
        //menu_escribe_linea_opcion(linea++,-1,1,textostats);
        zxvision_print_string_defaults(ventana,1,linea++,textostats);



        //menu_escribe_linea_opcion(linea++,-1,1,"Most used op. for each preffix");
        zxvision_print_string_defaults(ventana,1,linea++,"Most used op. for each preffix");

        opcode=util_stats_find_max_counter(stats_codsinpr);
        sprintf (textostats,"Op nopref:    %02XH: %u",opcode,util_stats_get_counter(stats_codsinpr,opcode) );
        //menu_escribe_linea_opcion(linea++,-1,1,textostats);
        zxvision_print_string_defaults(ventana,1,linea++,textostats);


        //Opcode
        menu_debug_cpu_stats_diss_complete_no_print(opcode,&dumpassembler[1],0,0);
        //menu_escribe_linea_opcion(linea++,-1,1,dumpassembler);
        zxvision_print_string_defaults(ventana,1,linea++,dumpassembler);




        opcode=util_stats_find_max_counter(stats_codpred);
        sprintf (textostats,"Op pref ED:   %02XH: %u",opcode,util_stats_get_counter(stats_codpred,opcode) );
        //menu_escribe_linea_opcion(linea++,-1,1,textostats);
        zxvision_print_string_defaults(ventana,1,linea++,textostats);


        //Opcode
        menu_debug_cpu_stats_diss_complete_no_print(opcode,&dumpassembler[1],237,0);
        //menu_escribe_linea_opcion(linea++,-1,1,dumpassembler);
        zxvision_print_string_defaults(ventana,1,linea++,dumpassembler);



        opcode=util_stats_find_max_counter(stats_codprcb);
        sprintf (textostats,"Op pref CB:   %02XH: %u",opcode,util_stats_get_counter(stats_codprcb,opcode) );
        //menu_escribe_linea_opcion(linea++,-1,1,textostats);
        zxvision_print_string_defaults(ventana,1,linea++,textostats);


        //Opcode
        menu_debug_cpu_stats_diss_complete_no_print(opcode,&dumpassembler[1],203,0);
        //menu_escribe_linea_opcion(linea++,-1,1,dumpassembler);
        zxvision_print_string_defaults(ventana,1,linea++,dumpassembler);




        opcode=util_stats_find_max_counter(stats_codprdd);
        sprintf (textostats,"Op pref DD:   %02XH: %u",opcode,util_stats_get_counter(stats_codprdd,opcode) );
        //menu_escribe_linea_opcion(linea++,-1,1,textostats);
        zxvision_print_string_defaults(ventana,1,linea++,textostats);

        //Opcode
        menu_debug_cpu_stats_diss_complete_no_print(opcode,&dumpassembler[1],221,0);
        //menu_escribe_linea_opcion(linea++,-1,1,dumpassembler);
        zxvision_print_string_defaults(ventana,1,linea++,dumpassembler);


        opcode=util_stats_find_max_counter(stats_codprfd);
        sprintf (textostats,"Op pref FD:   %02XH: %u",opcode,util_stats_get_counter(stats_codprfd,opcode) );
        //menu_escribe_linea_opcion(linea++,-1,1,textostats);
        zxvision_print_string_defaults(ventana,1,linea++,textostats);

        //Opcode
        menu_debug_cpu_stats_diss_complete_no_print(opcode,&dumpassembler[1],253,0);
        //menu_escribe_linea_opcion(linea++,-1,1,dumpassembler);
        zxvision_print_string_defaults(ventana,1,linea++,dumpassembler);


        opcode=util_stats_find_max_counter(stats_codprddcb);
        sprintf (textostats,"Op pref DDCB: %02XH: %u",opcode,util_stats_get_counter(stats_codprddcb,opcode) );
        //menu_escribe_linea_opcion(linea++,-1,1,textostats);
        zxvision_print_string_defaults(ventana,1,linea++,textostats);

        //Opcode
        menu_debug_cpu_stats_diss_complete_no_print(opcode,&dumpassembler[1],221,203);
        //menu_escribe_linea_opcion(linea++,-1,1,dumpassembler);
        zxvision_print_string_defaults(ventana,1,linea++,dumpassembler);



        opcode=util_stats_find_max_counter(stats_codprfdcb);
        sprintf (textostats,"Op pref FDCB: %02XH: %u",opcode,util_stats_get_counter(stats_codprfdcb,opcode) );
        //menu_escribe_linea_opcion(linea++,-1,1,textostats);
        zxvision_print_string_defaults(ventana,1,linea++,textostats);

        //Opcode
        menu_debug_cpu_stats_diss_complete_no_print(opcode,&dumpassembler[1],253,203);
        //menu_escribe_linea_opcion(linea++,-1,1,dumpassembler);
        zxvision_print_string_defaults(ventana,1,linea++,dumpassembler);


        //zxvision_draw_window_contents(ventana);


    }

    //Siempre hará el dibujado de contenido para evitar que cuando esta en background, otra ventana por debajo escriba algo,
    //y entonces como esta no redibuja siempre, al no escribir encima, se sobreescribe este contenido con el de otra ventana
    //En ventanas que no escriben siempre su contenido, siempre deberia estar zxvision_draw_window_contents que lo haga siempre
    zxvision_draw_window_contents(ventana);



}

zxvision_window menu_debug_cpu_resumen_stats_ventana;

void menu_debug_cpu_resumen_stats(MENU_ITEM_PARAMETERS)
{



	menu_espera_no_tecla();
	menu_reset_counters_tecla_repeticion();

    zxvision_window *ventana;

    ventana=&menu_debug_cpu_resumen_stats_ventana;

    //IMPORTANTE! no crear ventana si ya existe. Esto hay que hacerlo en todas las ventanas que permiten background.
    //si no se hiciera, se crearia la misma ventana, y en la lista de ventanas activas , al redibujarse,
    //la primera ventana repetida apuntaria a la segunda, que es el mismo puntero, y redibujaria la misma, y se quedaria en bucle colgado
    //zxvision_delete_window_if_exists(ventana);

    //Crear ventana si no existe
    if (!zxvision_if_window_already_exists(ventana)) {
        int x,y,ancho,alto,is_minimized,is_maximized,ancho_antes_minimize,alto_antes_minimize;

        if (!util_find_window_geometry("cpucompactstatistics",&x,&y,&ancho,&alto,&is_minimized,&is_maximized,&ancho_antes_minimize,&alto_antes_minimize)) {
            //x=menu_origin_x();
            //y=1;
            ancho=32;
            alto=18;

            x=menu_center_x()-ancho/2;
            y=menu_center_y()-alto/2;
        }

        //int originx=menu_origin_x();

        //zxvision_new_window(ventana,x,y,ancho,alto,ancho-1,alto-2,"CPU Compact Statistics");

        zxvision_new_window_gn_cim(ventana,x,y,ancho,alto,ancho-1,alto-2,"CPU Compact Statistics","cpucompactstatistics",
            is_minimized,is_maximized,ancho_antes_minimize,alto_antes_minimize);


        ventana->can_be_backgrounded=1;
        //indicar nombre del grabado de geometria
        //strcpy(ventana->geometry_name,"cpucompactstatistics");
        //restaurar estado minimizado de ventana
        //ventana->is_minimized=is_minimized;

    }

    //Si ya existe, activar esta ventana
    else {
        zxvision_activate_this_window(ventana);
    }

    //Y dibujar la ventana
    zxvision_draw_window(ventana);





    menu_debug_cpu_resumen_stats_overlay_window=ventana; //Decimos que el overlay lo hace sobre la ventana que tenemos aqui

    cpu_stats_valor_contador_segundo_anterior=contador_segundo;

    //Cambiamos funcion overlay de texto de menu
    //Se establece a la de funcion de onda + texto
    //cambio overlay
    zxvision_set_window_overlay(ventana,menu_debug_cpu_resumen_stats_overlay);


    //Toda ventana que este listada en zxvision_known_window_names_array debe permitir poder salir desde aqui
    //Se sale despues de haber inicializado overlay y de cualquier otra variable que necesite el overlay
    if (zxvision_currently_restoring_windows_on_start) {
        //printf ("Saliendo de ventana ya que la estamos restaurando en startup\n");
        return;
    }



	z80_byte tecla;

	do {
		tecla=zxvision_common_getkey_refresh();
		zxvision_handle_cursors_pgupdn(ventana,tecla);
		//printf ("tecla: %d\n",tecla);
	} while (tecla!=2 && tecla!=3);

	//Gestionar salir con tecla background

	menu_espera_no_tecla(); //Si no, se va al menu anterior.
	//En AY Piano por ejemplo esto no pasa aunque el estilo del menu es el mismo...





	//Grabar geometria ventana
	util_add_window_geometry_compact(ventana);


	if (tecla==3) {
		zxvision_message_put_window_background();
	}

	else {
		zxvision_destroy_window(ventana);
 	}


}

#endif

void zxvision_test_sleep_quarter(void)
{
	int previo_contador_segundo=contador_segundo;

	while (1) {
		menu_cpu_core_loop();
		if (previo_contador_segundo!=contador_segundo && (contador_segundo%250)==0) return;

		if (menu_get_pressed_key()!=0) return;

	}
}


void menu_zxvision_test(MENU_ITEM_PARAMETERS)
{

        //Desactivamos interlace - si esta. Con interlace la forma de onda se dibuja encima continuamente, sin borrar
        //z80_bit copia_video_interlaced_mode;
        //copia_video_interlaced_mode.v=video_interlaced_mode.v;

        //disable_interlace();


        menu_espera_no_tecla();


	//Prueba filesel
       /* char *filtros[2];

        filtros[0]="";
        filtros[1]=0;

        char nombredestino[PATH_MAX];
	nombredestino[0]=0;


	int retorno=menu_filesel("Select Target File",filtros,nombredestino);
	printf ("retorno: %d nombredestino: [%s]\n",retorno,nombredestino);
	return;*/



		//zxvision_generic_message_tooltip("pruebas", 30, 0, 0, generic_message_tooltip_return *retorno, const char * texto_format , ...)
		zxvision_generic_message_tooltip("Pruebas" , 1, 0 , 0, 0, 0, NULL, 0, 0, "Hola que tal como estas esto es una prueba de escribir texto. "
					"No se que mas poner pero me voy a empezar a repetir, "
					"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore "
					"et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip"
					" ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore "
					" eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia "
					"deserunt mollit anim id est laborum. Adios");
        //z80_byte acumulado;



        //Cambiamos funcion overlay de texto de menu
        //Se establece a la de funcion de audio waveform
	//set_menu_overlay_function(menu_audio_draw_sound_wave);

	zxvision_window ventana;

#define SOUND_ZXVISION_WAVE_X 1
#define SOUND_ZXVISION_WAVE_Y 3
#define SOUND_ZXVISION_WAVE_ANCHO 27
#define SOUND_ZXVISION_WAVE_ALTO 14

	int ancho_visible=SOUND_ZXVISION_WAVE_ANCHO;
	int alto_visible=SOUND_ZXVISION_WAVE_ALTO+4;

	int ancho_total=20;
	int alto_total=alto_visible+2;

	//menu_item *array_menu_audio_new_waveform;
      //  menu_item item_seleccionado;
        //int retorno_menu;



	  //Hay que redibujar la ventana desde este bucle
	zxvision_new_window(&ventana,SOUND_ZXVISION_WAVE_X,SOUND_ZXVISION_WAVE_Y-2,ancho_visible,alto_visible,
							ancho_total,alto_total,"ZXVision Test");
	zxvision_draw_window(&ventana);

	printf ("Created window\n");

	menu_espera_tecla();
	menu_espera_no_tecla();

	zxvision_draw_window_contents(&ventana);

	printf ("Drawn window contents\n");

	menu_espera_tecla();
	menu_espera_no_tecla();


	overlay_screen caracter;



	caracter.tinta=ESTILO_GUI_TINTA_NORMAL;
	caracter.papel=ESTILO_GUI_PAPEL_NORMAL;
	caracter.parpadeo=0;

	//Relleno pantalla
	z80_byte caracter_print=32;



	int x,y;

	for (y=0;y<alto_total;y++) {
		for (x=0;x<ancho_total;x++) {
			caracter.caracter=caracter_print;
			zxvision_print_char(&ventana,x,y,&caracter);

			caracter_print++;
			if (caracter_print>126) caracter_print=32;
		}
	}

	caracter.caracter='A';

	zxvision_print_char(&ventana,0,0,&caracter);

	caracter.caracter='B';

	zxvision_print_char(&ventana,0,1,&caracter);

	zxvision_print_string(&ventana,0,0,ESTILO_GUI_TINTA_NORMAL,ESTILO_GUI_PAPEL_NORMAL,0," Texto leyenda 1 ");
	zxvision_print_string(&ventana,0,1,ESTILO_GUI_TINTA_NORMAL,ESTILO_GUI_PAPEL_NORMAL,0," Texto leyenda 2 ");
	zxvision_print_string(&ventana,0,2,ESTILO_GUI_TINTA_NORMAL,ESTILO_GUI_PAPEL_NORMAL,0," Texto leyenda 3 ");

	zxvision_print_string(&ventana,2,3,ESTILO_GUI_PAPEL_NORMAL,ESTILO_GUI_TINTA_NORMAL,1," This is a test ");

	zxvision_print_string(&ventana,2,4,ESTILO_GUI_PAPEL_NORMAL,ESTILO_GUI_TINTA_NORMAL,1," Press a key ");
	zxvision_print_string(&ventana,2,5,ESTILO_GUI_PAPEL_NORMAL,ESTILO_GUI_TINTA_NORMAL,1," to next step ");

	zxvision_print_string(&ventana,2,6,ESTILO_GUI_TINTA_NORMAL,ESTILO_GUI_PAPEL_NORMAL,0," --^^flash^^--");
	zxvision_print_string(&ventana,2,7,ESTILO_GUI_TINTA_NORMAL,ESTILO_GUI_PAPEL_NORMAL,0," --~~inverse--");
	zxvision_print_string(&ventana,2,8,ESTILO_GUI_TINTA_NORMAL,ESTILO_GUI_PAPEL_NORMAL,0," --$$2ink--");


                    /*    menu_add_item_menu_inicial_format(&array_menu_audio_new_waveform,MENU_OPCION_NORMAL,menu_audio_new_waveform_shape,NULL,"Change wave ~~Shape");
                        menu_add_item_menu_shortcut(array_menu_audio_new_waveform,'s');

                        //Evito tooltips en los menus tabulados que tienen overlay porque al salir el tooltip detiene el overlay
                        //menu_add_item_menu_tooltip(array_menu_audio_new_waveform,"Change wave Shape");
                        menu_add_item_menu_ayuda(array_menu_audio_new_waveform,"Change wave Shape: simple line or vertical fill");
						//0123456789
						// Change wave Shape

			menu_add_item_menu_tabulado(array_menu_audio_new_waveform,1,0);





		//Nombre de ventana solo aparece en el caso de stdout
                retorno_menu=menu_dibuja_menu_no_title_lang(&audio_new_waveform_opcion_seleccionada,&item_seleccionado,array_menu_audio_new_waveform,"Waveform" );*/


	ventana.upper_margin=2;
	ventana.lower_margin=1;


	menu_espera_tecla();
	menu_espera_no_tecla();

	zxvision_draw_window_contents(&ventana);

	menu_espera_tecla();
	menu_espera_no_tecla();

	//Jugar con offset
/*	int i;

	for (i=0;i<7;i++) {
		zxvision_set_offset_x(&ventana,i);

		zxvision_draw_window_contents(&ventana);

		printf ("Offset x %d\n",i);

		menu_espera_tecla();
		menu_espera_no_tecla();
	}


	for (i=0;i<7;i++) {
		zxvision_set_offset_y(&ventana,i);

		zxvision_draw_window_contents(&ventana);

		printf ("Offset y %d\n",i);

		menu_espera_tecla();
		menu_espera_no_tecla();
	}



	for (i=0;i<10;i++) {
		zxvision_set_x_position(&ventana,i);

		printf ("Move x %d\n",i);

		menu_espera_tecla();
		menu_espera_no_tecla();
	}

	for (i=0;i<10;i++) {
		zxvision_set_y_position(&ventana,i);

		printf ("Move y %d\n",i);

		menu_espera_tecla();
		menu_espera_no_tecla();
	}

	zxvision_set_x_position(&ventana,0);
	zxvision_set_y_position(&ventana,0);

	for (i=25;i<35;i++) {
		zxvision_set_visible_width(&ventana,i);

		printf ("width %d\n",i);

		menu_espera_tecla();
		menu_espera_no_tecla();
	}

	for (i=18;i<28;i++) {
		zxvision_set_visible_height(&ventana,i);

		printf ("height %d\n",i);

		menu_espera_tecla();
		menu_espera_no_tecla();
	}


	for (i=5;i>=0;i--) {
		zxvision_set_visible_width(&ventana,i);

		printf ("width %d\n",i);

		menu_espera_tecla();
		menu_espera_no_tecla();
	}

	zxvision_set_visible_width(&ventana,20);

	for (i=5;i>=0;i--) {
		zxvision_set_visible_height(&ventana,i);

		printf ("height %d\n",i);

		menu_espera_tecla();
		menu_espera_no_tecla();
	}


*/
	zxvision_print_string(&ventana,2,3,ESTILO_GUI_PAPEL_NORMAL,ESTILO_GUI_TINTA_NORMAL,1," Use cursors ");
	zxvision_print_string(&ventana,2,4,ESTILO_GUI_PAPEL_NORMAL,ESTILO_GUI_TINTA_NORMAL,1," to move offset ");
	zxvision_print_string(&ventana,2,5,ESTILO_GUI_PAPEL_NORMAL,ESTILO_GUI_TINTA_NORMAL,1," QAOP size");
	zxvision_print_string(&ventana,2,6,ESTILO_GUI_PAPEL_NORMAL,ESTILO_GUI_TINTA_NORMAL,1," ESC exit ");


	//Rebotar
	int contador=0;

	int xpos=0;
	int ypos=0;

	int incx=+1;
	int incy=+1;

	int offsetx=0;
	int offsety=0;

	int ancho=22;
	int alto=10;

	zxvision_set_visible_height(&ventana,alto);
	zxvision_set_visible_width(&ventana,ancho);

	z80_byte tecla=0;

	//Salir con ESC
	while (tecla!=2) {

		zxvision_set_offset_x(&ventana,offsetx);
		zxvision_set_offset_y(&ventana,offsety);

		zxvision_set_x_position(&ventana,xpos);
		zxvision_set_y_position(&ventana,ypos);

		zxvision_test_sleep_quarter();

		tecla=menu_get_pressed_key();
		//Cambio offset con cursores
		if (tecla==8) {
			offsetx--;
			printf ("Decrement offset x to %d\n",offsetx);
		}

		if (tecla==9) {
			offsetx++;
			printf ("Increment offset x to %d\n",offsetx);
		}

		if (tecla==10) {
			offsety++;
			printf ("Increment offset y to %d\n",offsety);
		}

		if (tecla==11) {
			offsety--;
			printf ("Decrement offset y to %d\n",offsety);
		}

		//Cambio tamanyo
		if (tecla=='a' && ypos+alto<24) {
			alto++;
			printf ("Increment height to %d\n",alto);
			zxvision_set_visible_height(&ventana,alto);
		}

		if (tecla=='q' && alto>1) {
			alto--;
			printf ("Decrement height to %d\n",alto);
			zxvision_set_visible_height(&ventana,alto);
		}

		if (tecla=='p' && xpos+ancho<32) {
			ancho++;
			printf ("Increment width to %d\n",ancho);
			zxvision_set_visible_width(&ventana,ancho);
		}

		if (tecla=='o' && ancho>7) {
			ancho--;
			printf ("Decrement width to %d\n",ancho);
			zxvision_set_visible_width(&ventana,ancho);
		}

		xpos +=incx;
		if (xpos+ancho>=32 || xpos<=0) {
			incx=-incx;
		}

		ypos +=incy;
		if (ypos+alto>=24 || ypos<=0) {
			incy=-incy;
		}

		contador++;

		if (tecla!=0) menu_espera_no_tecla();

	}


	zxvision_print_string(&ventana,1,5,ESTILO_GUI_PAPEL_NORMAL,ESTILO_GUI_TINTA_NORMAL,1," Use mouse    ");
	zxvision_print_string(&ventana,1,6,ESTILO_GUI_PAPEL_NORMAL,ESTILO_GUI_TINTA_NORMAL,1," to move and   ");
	zxvision_print_string(&ventana,1,7,ESTILO_GUI_PAPEL_NORMAL,ESTILO_GUI_TINTA_NORMAL,1," resize window  ");
	zxvision_print_string(&ventana,1,8,ESTILO_GUI_PAPEL_NORMAL,ESTILO_GUI_TINTA_NORMAL,1," Right button exits ");

	zxvision_draw_window_contents(&ventana);

	tecla=0;

	while (!mouse_right) {
		//menu_espera_tecla();
		tecla=menu_get_pressed_key();
		//Comprobar eventos raton
		menu_cpu_core_loop();
		//zxvision_handle_mouse_events(&ventana);
	}

    cls_menu_overlay();
	zxvision_destroy_window(&ventana);





}


//Indica a la funcion de overlay cual es la ventana
zxvision_window *menu_about_core_statistics_overlay_window;

//Contador de segundo para hacer que el overlay solo se redibuje un numero de veces por segundo y no siempre
int menu_core_statistics_contador_segundo_anterior;

int core_statistics_previo_audio_buffer=0;

int core_statistics_previo_audio_record_buffer=0;

int core_statistics_previo_cpu=0;

int core_statistics_last_perc_audio=0;

int core_statistics_last_perc_dropped=0;

int core_statistics_ultimo_cpu_use_mostrado=0;

//Linea donde va ubicado el texto. Inicialmente no sabemos donde estara
int core_statistics_linea_mostrar_estadisticas_chars=-1;

//La funcion de overlay
void menu_about_core_statistics_overlay_window_overlay(void)
{


    menu_speech_set_tecla_pulsada(); //Si no, envia continuamente todo ese texto a speech

    //si ventana minimizada, no ejecutar todo el codigo de overlay
    if (menu_about_core_statistics_overlay_window->is_minimized) return;

    //printf("overlay core statistics %d\n",contador_segundo);

    zxvision_window *ventana;

    ventana=menu_about_core_statistics_overlay_window;



    //esto hara ejecutar esto 2 veces por segundo
    if ( ((contador_segundo%500) == 0 && menu_core_statistics_contador_segundo_anterior!=contador_segundo) ) {

        menu_core_statistics_contador_segundo_anterior=contador_segundo;
        //printf ("Refrescando. contador_segundo=%d\n",contador_segundo);

        int linea=0;
        char texto_buffer[100];


        //Empezar con espacio
        texto_buffer[0]=' ';


/*

Nota: calcular el tiempo entre ejecuciones de cada opcode no penaliza mucho el uso de cpu real.
Ejemplo:
--vo null --machine 48k

Sin calcular ese tiempo: 9% cpu
Calculando ese tiempo: 12% cpu

*/


//Ultimo intervalo de tiempo
//long core_cpu_timer_frame_difftime;
//Media de todos los intervalos
//long core_cpu_timer_frame_media=0;

        long valor_mostrar;
        valor_mostrar=core_cpu_timer_frame_difftime;
        //controlar maximos
        if (valor_mostrar>999999) valor_mostrar=999999;
            //01234567890123456789012345678901
            // Last core frame: 999999 us
        sprintf (texto_buffer,"Last core frame:     %6ld us",valor_mostrar);
        //menu_escribe_linea_opcion(linea++,-1,1,texto_buffer);
        zxvision_print_string_defaults(ventana,1,linea++,texto_buffer);

        valor_mostrar=core_cpu_timer_frame_media;
        //controlar maximos
        if (valor_mostrar>999999) valor_mostrar=999999;
        //01234567890123456789012345678901
            // Last core frame: 999999 us
        sprintf (texto_buffer," Average:   %6ld us",valor_mostrar);
        //menu_escribe_linea_opcion(linea++,-1,1,texto_buffer);
        zxvision_print_string_defaults(ventana,1,linea++,texto_buffer);


        valor_mostrar=core_cpu_timer_refresca_pantalla_difftime;
        //controlar maximos
        if (valor_mostrar>999999) valor_mostrar=999999;
        //01234567890123456789012345678901
        // Last render display: 999999 us
        sprintf (texto_buffer,"Last full render:    %6ld us",valor_mostrar);
        //menu_escribe_linea_opcion(linea++,-1,1,texto_buffer);
        zxvision_print_string_defaults(ventana,1,linea++,texto_buffer);

        valor_mostrar=core_cpu_timer_refresca_pantalla_media;
        //controlar maximos
        if (valor_mostrar>999999) valor_mostrar=999999;
        //01234567890123456789012345678901
            // Last core refresca_pantalla: 999999 us
        sprintf (texto_buffer," Average:   %6ld us",valor_mostrar);
        //menu_escribe_linea_opcion(linea++,-1,1,texto_buffer);
        zxvision_print_string_defaults(ventana,1,linea++,texto_buffer);


        valor_mostrar=core_cpu_timer_each_frame_difftime;
        //controlar maximos
        if (valor_mostrar>999999) valor_mostrar=999999;
        //01234567890123456789012345678901
        // Time between frames: 999999 us
        sprintf (texto_buffer,"Time between frames: %6ld us",valor_mostrar);
        //menu_escribe_linea_opcion(linea++,-1,1,texto_buffer);
        zxvision_print_string_defaults(ventana,1,linea++,texto_buffer);

        valor_mostrar=core_cpu_timer_each_frame_media;
        //controlar maximos
        if (valor_mostrar>999999) valor_mostrar=999999;
        //01234567890123456789012345678901
            // Last core each_frame: 999999 us
        sprintf (texto_buffer," Average:   %6ld us",valor_mostrar);
        //menu_escribe_linea_opcion(linea++,-1,1,texto_buffer);
        zxvision_print_string_defaults(ventana,1,linea++,texto_buffer);

        //menu_escribe_linea_opcion(linea++,-1,1," (ideal):  20000 us");
        zxvision_print_string_defaults(ventana,1,linea++," (expected): 20000 us");

        sprintf (texto_buffer,"Total video frames: %d",stats_frames_total);
        zxvision_print_string_defaults(ventana,1,linea++,texto_buffer);

        sprintf (texto_buffer," Drawn: %d",stats_frames_total_drawn);
        zxvision_print_string_defaults(ventana,1,linea++,texto_buffer);

        int perc_dropped;

        //Evitar división por cero
        if (stats_frames_total==0) perc_dropped=0;

        else perc_dropped=(stats_frames_total_dropped*100)/stats_frames_total;

        sprintf (texto_buffer," Dropped: %d (%3d%%)",stats_frames_total_dropped,perc_dropped);
        core_statistics_last_perc_dropped=perc_dropped;
        zxvision_print_string_defaults(ventana,1,linea++,texto_buffer);



        //Dejamos hueco para estadisticas de chars
        core_statistics_linea_mostrar_estadisticas_chars=linea;
        linea++;
        linea++;


        int media_cpu=sensor_get_percentaje_value("instant_avg_cpu");


        //Playback audio buffer
        int tamanyo_buffer_audio,posicion_buffer_audio;
		audio_get_buffer_info(&tamanyo_buffer_audio,&posicion_buffer_audio);

        int perc_audio;
        //mostrar una barra de llenado del buffer
        //usa las mismas funciones de volumen de AY chip donde el maximo es 15
        //int barra_volumen;

        if (tamanyo_buffer_audio==0) {
            perc_audio=0;
            //barra_volumen=15;
        }

        else {
            perc_audio=(posicion_buffer_audio*100)/tamanyo_buffer_audio;
            //barra_volumen=(posicion_buffer_audio*15)/tamanyo_buffer_audio;
        }

        char buf_volumen_canal[32];
        //menu_string_volumen(buf_volumen_canal,barra_volumen,core_statistics_previo_audio_buffer);

        //core_statistics_previo_audio_buffer=barra_volumen;

        core_statistics_previo_audio_buffer=menu_string_volumen_maxmin(buf_volumen_canal,posicion_buffer_audio,
                                                    core_statistics_previo_audio_buffer,tamanyo_buffer_audio);


        sprintf (texto_buffer,"Audio Buffer%s: %d/%d (%3d%%) [%s]",
                    (si_audio_silenced() ? " (Silenced)" : ""),
                    posicion_buffer_audio,tamanyo_buffer_audio,perc_audio,buf_volumen_canal);

        core_statistics_last_perc_audio=perc_audio;

        zxvision_print_string_defaults_fillspc(ventana,1,linea++,texto_buffer);


        //Record audio buffer
        //int tamanyo_buffer_audio,posicion_buffer_audio;
		//audio_get_buffer_info(&tamanyo_buffer_audio,&posicion_buffer_audio);
        tamanyo_buffer_audio=audiorecord_input_return_fifo_total_size();
        posicion_buffer_audio=audiorecord_input_fifo_return_size();

        //int perc_audio;
        //mostrar una barra de llenado del buffer
        //usa las mismas funciones de volumen de AY chip donde el maximo es 15
        //int barra_volumen;

        if (tamanyo_buffer_audio==0) {
            perc_audio=0;
            //barra_volumen=15;
        }

        else {
            perc_audio=(posicion_buffer_audio*100)/tamanyo_buffer_audio;
            //barra_volumen=(posicion_buffer_audio*15)/tamanyo_buffer_audio;
        }

        //char buf_volumen_canal[32];
        //menu_string_volumen(buf_volumen_canal,barra_volumen,core_statistics_previo_audio_buffer);

        //core_statistics_previo_audio_buffer=barra_volumen;

        core_statistics_previo_audio_record_buffer=menu_string_volumen_maxmin(buf_volumen_canal,posicion_buffer_audio,
                                                    core_statistics_previo_audio_record_buffer,tamanyo_buffer_audio);


        sprintf (texto_buffer,"Record Buffer: %6d/%6d (%3d%%) [%s]",
                    posicion_buffer_audio,tamanyo_buffer_audio,perc_audio,buf_volumen_canal);
        zxvision_print_string_defaults_fillspc(ventana,1,linea++,texto_buffer);

        sprintf (texto_buffer," %s",(!audio_is_recording_input ? "(Not Recording)" : "(Recording)"));
        zxvision_print_string_defaults_fillspc(ventana,1,linea++,texto_buffer);

		//Uso cpu no se ve en windows
#ifndef MINGW
        if (screen_show_cpu_usage.v) {

            //mostrar una barra de llenado del buffer
            //usa las mismas funciones de volumen de AY chip donde el maximo es 15
            char buf_barra_cpu[32];
            /*
            int barra_cpu;

            barra_cpu=(media_cpu*15)/100;


            menu_string_volumen(buf_barra_cpu,barra_cpu,core_statistics_previo_cpu);

            core_statistics_previo_cpu=barra_cpu;
            */

            core_statistics_previo_cpu=menu_string_volumen_maxmin(buf_barra_cpu,media_cpu,core_statistics_previo_cpu,100);
            core_statistics_ultimo_cpu_use_mostrado=media_cpu;



            sprintf(texto_buffer,"Average CPU Use: %d%% [%s]",media_cpu,buf_barra_cpu);
            zxvision_print_string_defaults(ventana,1,linea++,texto_buffer);
        }
#endif


    }


    //Estadisticas chars dibujar siempre
    //Linea donde va ubicado el texto
    if (core_statistics_linea_mostrar_estadisticas_chars>=0) {
        int perc_chars;

        if (stats_normal_overlay_menu_total_chars==0) perc_chars=0;
        else perc_chars=(stats_normal_overlay_menu_drawn_chars*100)/stats_normal_overlay_menu_total_chars;


        zxvision_print_string_defaults_fillspc_format(ventana,1,core_statistics_linea_mostrar_estadisticas_chars,
            "Menu cache. Total Chars: %d",
            stats_normal_overlay_menu_total_chars);

        zxvision_print_string_defaults_fillspc_format(ventana,1,core_statistics_linea_mostrar_estadisticas_chars+1,
            " Drawn: %5d (%3d%%)",
            stats_normal_overlay_menu_drawn_chars,perc_chars);

    }

    //Medidores de rendimiento

    int fila_texto=17;
    int margen_horizontal=ZXVISION_WIDGET_TYPE_SPEEDOMETER_LINE_LENGTH;

    int longitud_linea=ZXVISION_WIDGET_TYPE_SPEEDOMETER_LINE_LENGTH;

    //char buffer_texto_meters[30];



    //int grados;
    //0 grados=0%
    //180 grados=100%



    int yorigen_linea=(fila_texto*menu_char_height)+longitud_linea+menu_char_height*2;

    //CPU USE
    int pos_x=1;
    int xorigen_linea=menu_char_width+longitud_linea; //Para ajustarlo por la derecha

    int color=ESTILO_GUI_COLOR_WAVEFORM;
    if (core_statistics_ultimo_cpu_use_mostrado>=75) color=ESTILO_GUI_COLOR_AVISO;

    zxvision_print_string_defaults(ventana,pos_x,fila_texto,"CPU");
    zxvision_widgets_erase_speedometer(ventana,xorigen_linea,yorigen_linea);
    zxvision_widgets_draw_speedometer_common(ventana,xorigen_linea,yorigen_linea,core_statistics_ultimo_cpu_use_mostrado,color,color);


    //AUDIO BUFFER
    pos_x += (longitud_linea*2+margen_horizontal)/menu_char_width;

    xorigen_linea=xorigen_linea+longitud_linea*2+margen_horizontal; //A la derecha del anterior

    color=ESTILO_GUI_COLOR_WAVEFORM;
    //es tan malo como que este lleno como vacio
    if (core_statistics_last_perc_audio>=90 || core_statistics_last_perc_audio<=10) color=ESTILO_GUI_COLOR_AVISO;

    zxvision_print_string_defaults(ventana,pos_x,fila_texto,"Audio");
    zxvision_widgets_erase_speedometer(ventana,xorigen_linea,yorigen_linea);
    zxvision_widgets_draw_speedometer_common(ventana,xorigen_linea,yorigen_linea,core_statistics_last_perc_audio,color,color);


    //DROPPED FRAMES
    pos_x += (longitud_linea*2+margen_horizontal)/menu_char_width;

    xorigen_linea=xorigen_linea+longitud_linea*2+margen_horizontal; //A la derecha del anterior

    color=ESTILO_GUI_COLOR_WAVEFORM;
    if (core_statistics_last_perc_dropped>=75) color=ESTILO_GUI_COLOR_AVISO;

    zxvision_print_string_defaults(ventana,pos_x,fila_texto,"Dropped");
    zxvision_widgets_erase_speedometer(ventana,xorigen_linea,yorigen_linea);
    zxvision_widgets_draw_speedometer_common(ventana,xorigen_linea,yorigen_linea,core_statistics_last_perc_dropped,color,color);



    //Siempre hará el dibujado de contenido para evitar que cuando esta en background, otra ventana por debajo escriba algo,
    //y entonces como esta no redibuja siempre, al no escribir encima, se sobreescribe este contenido con el de otra ventana
    //En ventanas que no escriben siempre su contenido, siempre deberia estar zxvision_draw_window_contents que lo haga siempre
    zxvision_draw_window_contents(ventana);
}

//La ventana tal cual que creamos. Es la estructura, no un puntero
zxvision_window zxvision_window_core_statistics;

void menu_about_core_statistics(MENU_ITEM_PARAMETERS)
{

    menu_espera_no_tecla();
	menu_reset_counters_tecla_repeticion();

    if (!menu_multitarea) {
        menu_warn_message("This window needs multitask enabled");
        return;
    }

    //Nuestro puntero apunta a la estructura que hay fuera, por comodidad de usar el nombre de puntero "ventana"
    zxvision_window *ventana;
    ventana=&zxvision_window_core_statistics;

    //IMPORTANTE! no crear ventana si ya existe. Esto hay que hacerlo en todas las ventanas que permiten background.
    //si no se hiciera, se crearia la misma ventana, y en la lista de ventanas activas , al redibujarse,
    //la primera ventana repetida apuntaria a la segunda, que es el mismo puntero, y redibujaria la misma, y se quedaria en bucle colgado
    //zxvision_delete_window_if_exists(ventana);

    //Crear ventana si no existe
    if (!zxvision_if_window_already_exists(ventana)) {

        int x_ventana,y_ventana,ancho_ventana,alto_ventana,is_minimized,is_maximized,ancho_antes_minimize,alto_antes_minimize;

        //Recuperar geometria
        if (!util_find_window_geometry("corestatistics",&x_ventana,&y_ventana,&ancho_ventana,&alto_ventana,&is_minimized,&is_maximized,&ancho_antes_minimize,&alto_antes_minimize)) {
            alto_ventana=18;
            ancho_ventana=32;

            x_ventana=menu_center_x()-ancho_ventana/2;
            y_ventana=menu_center_y()-alto_ventana/2;
        }

        //Crear ventana
        //zxvision_new_window(ventana,x_ventana,y_ventana,ancho_ventana,alto_ventana,ancho_ventana-1,alto_ventana-2,"Core Statistics");

        zxvision_new_window_gn_cim(ventana,x_ventana,y_ventana,ancho_ventana,alto_ventana,ancho_ventana-1,alto_ventana-2,"Core Statistics","corestatistics",
            is_minimized,is_maximized,ancho_antes_minimize,alto_antes_minimize);

        //Se puede ir a background
        ventana->can_be_backgrounded=1;
        //indicar nombre del grabado de geometria
        //strcpy(ventana->geometry_name,"corestatistics");
        //restaurar estado minimizado de ventana
        //ventana->is_minimized=is_minimized;

    }

    //Si ya existe, activar esta ventana
    else {
        zxvision_activate_this_window(ventana);
    }

    //Y dibujar la ventana
    zxvision_draw_window(ventana);


    menu_about_core_statistics_overlay_window=ventana; //Decimos que el overlay lo hace sobre la ventana que tenemos aqui

    //Cambiamos funcion overlay de texto de menu
    //cambio overlay
    zxvision_set_window_overlay(ventana,menu_about_core_statistics_overlay_window_overlay);

    //Toda ventana que este listada en zxvision_known_window_names_array debe permitir poder salir desde aqui
    //Se sale despues de haber inicializado overlay y de cualquier otra variable que necesite el overlay
    if (zxvision_currently_restoring_windows_on_start) {
        //printf ("Saliendo de ventana ya que la estamos restaurando en startup\n");
        return;
    }

    z80_byte tecla;

    //Y esperar escape (2) o tecla background (3)
    do {
            tecla=zxvision_common_getkey_refresh();
            zxvision_handle_cursors_pgupdn(ventana,tecla);
            //printf ("tecla: %d\n",tecla);
    } while (tecla!=2 && tecla!=3);




    util_add_window_geometry_compact(ventana);

    if (tecla==3) {
            zxvision_message_put_window_background();
    }

    else {
            zxvision_destroy_window(ventana);
    }


}




int ayregisters_previo_valor_volume_A[MAX_AY_CHIPS];
int ayregisters_previo_valor_volume_B[MAX_AY_CHIPS];
int ayregisters_previo_valor_volume_C[MAX_AY_CHIPS];
int ayregisters_previo_valor_volume_noise;

	int menu_ayregisters_valor_contador_segundo_anterior;

zxvision_window *menu_ay_registers_overlay_window;

void menu_ay_registers_overlay(void)
{

	//NOTA: //Hemos de suponer que current window es esta de ay registers




	menu_speech_set_tecla_pulsada(); //Si no, envia continuamente todo ese texto a speech


    //si ventana minimizada, no ejecutar todo el codigo de overlay
    if (menu_ay_registers_overlay_window->is_minimized) return;



	char volumen[32],textotono[32];
	char textovolumen[35]; //32+3 de posible color rojo del maximo


	int total_chips=audio_get_total_chips();

	//Como maximo mostrarmos 3 canales ay
	if (total_chips>3) total_chips=3;

	int chip;

	int linea=0;


	int vol_A[MAX_AY_CHIPS],vol_B[MAX_AY_CHIPS],vol_C[MAX_AY_CHIPS],vol_noise;


	if (sn_chip_present.v) {

		//Para chip SN76489A de coleco y sg1000 y sms
		total_chips=1;



      		vol_A[0]=sn_chip_registers[6] & 15;
        	vol_B[0]=sn_chip_registers[7] & 15;
        	vol_C[0]=sn_chip_registers[8] & 15;
			vol_noise=sn_chip_registers[10] & 15;

			//Controlar limites, dado que las variables entran sin inicializar
			if (ayregisters_previo_valor_volume_A[0]>16) ayregisters_previo_valor_volume_A[0]=16;
			if (ayregisters_previo_valor_volume_B[0]>16) ayregisters_previo_valor_volume_B[0]=16;
			if (ayregisters_previo_valor_volume_C[0]>16) ayregisters_previo_valor_volume_C[0]=16;
			if (ayregisters_previo_valor_volume_noise>16) ayregisters_previo_valor_volume_noise=16;


			ayregisters_previo_valor_volume_A[0]=menu_decae_ajusta_valor_volumen(ayregisters_previo_valor_volume_A[0],vol_A[0]);
			ayregisters_previo_valor_volume_B[0]=menu_decae_ajusta_valor_volumen(ayregisters_previo_valor_volume_B[0],vol_B[0]);
			ayregisters_previo_valor_volume_C[0]=menu_decae_ajusta_valor_volumen(ayregisters_previo_valor_volume_C[0],vol_C[0]);
			ayregisters_previo_valor_volume_noise=menu_decae_ajusta_valor_volumen(ayregisters_previo_valor_volume_noise,vol_noise);


			z80_byte volumen_canal;

			volumen_canal=15 - (sn_chip_registers[6] & 15);
			menu_string_volumen(volumen,volumen_canal,ayregisters_previo_valor_volume_A[0]);
			sprintf (textovolumen,"Volume A: %s",volumen);
			//menu_escribe_linea_opcion(linea++,-1,1,textovolumen);
			zxvision_print_string_defaults(menu_ay_registers_overlay_window,1,linea++,textovolumen);

			volumen_canal=15 - (sn_chip_registers[7] & 15);
			menu_string_volumen(volumen,volumen_canal,ayregisters_previo_valor_volume_B[0]);
			sprintf (textovolumen,"Volume B: %s",volumen);
			//menu_escribe_linea_opcion(linea++,-1,1,textovolumen);
			zxvision_print_string_defaults(menu_ay_registers_overlay_window,1,linea++,textovolumen);

			volumen_canal=15 - (sn_chip_registers[8] & 15);
			menu_string_volumen(volumen,volumen_canal,ayregisters_previo_valor_volume_C[0]);
			sprintf (textovolumen,"Volume C: %s",volumen);
			//menu_escribe_linea_opcion(linea++,-1,1,textovolumen);
			zxvision_print_string_defaults(menu_ay_registers_overlay_window,1,linea++,textovolumen);


			volumen_canal=15 - (sn_chip_registers[10] & 15);
			menu_string_volumen(volumen,volumen_canal,ayregisters_previo_valor_volume_noise);
								//"Volume C: %s"
			sprintf (textovolumen,"V Noise:  %s",volumen);
			//menu_escribe_linea_opcion(linea++,-1,1,textovolumen);
			zxvision_print_string_defaults(menu_ay_registers_overlay_window,1,linea++,textovolumen);



			int freq_a=sn_retorna_frecuencia(0);
			int freq_b=sn_retorna_frecuencia(1);
			int freq_c=sn_retorna_frecuencia(2);
			sprintf (textotono,"Channel A:  %3s %7d Hz",get_note_name(freq_a),freq_a);
			//menu_escribe_linea_opcion(linea++,-1,1,textotono);
			zxvision_print_string_defaults(menu_ay_registers_overlay_window,1,linea++,textotono);

			sprintf (textotono,"Channel B:  %3s %7d Hz",get_note_name(freq_b),freq_b);
			//menu_escribe_linea_opcion(linea++,-1,1,textotono);
			zxvision_print_string_defaults(menu_ay_registers_overlay_window,1,linea++,textotono);

			sprintf (textotono,"Channel C:  %3s %7d Hz",get_note_name(freq_c),freq_c);
			zxvision_print_string_defaults(menu_ay_registers_overlay_window,1,linea++,textotono);


					                        //Tipo ruido
											/*
+--+--+--+--+--+--+--+--+
|1 |1 |1 |0 |xx|FB|M1|M0|
+--+--+--+--+--+--+--+--+

FB= Feedback:

0= 'Periodic' noise
1= 'white' noise

The white noise sounds, well, like white noise.
The periodic noise is intresting.  Depending on the frequency, it can
sound very tonal and smooth.

M1-M0= mode bits:

00= Fosc/512  Very 'hissy'; like grease frying
01= Fosc/1024 Slightly lower
10= Fosc/2048 More of a high rumble
11= output of tone generator #3
											*/

						z80_byte noise_control=sn_chip_registers[9];


						sprintf (textotono,"Noise Type: %s",(noise_control & 4 ? "White" : "Periodic"));
						zxvision_print_string_defaults_fillspc(menu_ay_registers_overlay_window,1,linea++,textotono);


						sprintf (textotono,"Noise Mode: %d",noise_control & 3);
						zxvision_print_string_defaults(menu_ay_registers_overlay_window,1,linea++,textotono);



											/*
                        int freq_temp=ay_3_8912_registros[chip][6] & 31;
                        //printf ("Valor registros ruido : %d Hz\n",freq_temp);
                        freq_temp=freq_temp*16;

                        //controlamos divisiones por cero
                        if (!freq_temp) freq_temp++;

                        int freq_ruido=FRECUENCIA_NOISE/freq_temp;

                        sprintf (textotono,"Frequency Noise: %6d Hz",freq_ruido);
                        //menu_escribe_linea_opcion(linea++,-1,1,textotono);
						zxvision_print_string_defaults(menu_ay_registers_overlay_window,1,linea++,textotono);
							*/




	}

    else if (i8049_chip_present) {
        total_chips=1;

            int volumen_pitch1;

            if (ql_audio_playing) volumen_pitch1=15;
            else volumen_pitch1=0;

      		vol_A[0]=volumen_pitch1;


			//Controlar limites, dado que las variables entran sin inicializar
			if (ayregisters_previo_valor_volume_A[0]>16) ayregisters_previo_valor_volume_A[0]=16;



			ayregisters_previo_valor_volume_A[0]=menu_decae_ajusta_valor_volumen(ayregisters_previo_valor_volume_A[0],vol_A[0]);



			z80_byte volumen_canal;

			volumen_canal=volumen_pitch1;
			menu_string_volumen(volumen,volumen_canal,ayregisters_previo_valor_volume_A[0]);
			sprintf (textovolumen,"Volume: %s",volumen);
			//menu_escribe_linea_opcion(linea++,-1,1,textovolumen);
			zxvision_print_string_defaults(menu_ay_registers_overlay_window,1,linea++,textovolumen);


        int freq_a=ql_ipc_get_frecuency_sound_current_pitch();

        sprintf (textotono,"Frequency: %3s %7d Hz",get_note_name(freq_a),freq_a);
        //menu_escribe_linea_opcion(linea++,-1,1,textotono);
        zxvision_print_string_defaults(menu_ay_registers_overlay_window,1,linea++,textotono);

        zxvision_print_string_defaults(menu_ay_registers_overlay_window,1,linea++,(ql_audio_playing ? "Playing" : "Stopped") );


    /*
    Formato del mensaje ipc:

    8 bits pitch 1
    8 bits pitch 2
    16 bits  interval between steps (grad_x)
    16 bits duration
    4 bits step in pitch (grad_y)
    4 bits wrap
    4 bits randomness of step
    4 bits fuzziness


*/

        zxvision_print_string_defaults_fillspc(menu_ay_registers_overlay_window,1,linea++,"");

        //Nota: imprimo todos con %5d para que esten alineados, independientemente si son valores de 4, 8 o 16 bis
        sprintf (textotono,"Pitch1:         %5d",ql_audio_pitch1);
        zxvision_print_string_defaults_fillspc(menu_ay_registers_overlay_window,1,linea++,textotono);
        sprintf (textotono,"Pitch2:         %5d",ql_audio_pitch2);
        zxvision_print_string_defaults_fillspc(menu_ay_registers_overlay_window,1,linea++,textotono);
        sprintf (textotono,"Interval steps: %5d",ql_audio_grad_x);
        zxvision_print_string_defaults_fillspc(menu_ay_registers_overlay_window,1,linea++,textotono);
        sprintf (textotono,"Duration:       %5d",ql_audio_duration);
        zxvision_print_string_defaults_fillspc(menu_ay_registers_overlay_window,1,linea++,textotono);
        sprintf (textotono,"Step in pitch:  %5d",ql_audio_grad_y);
        zxvision_print_string_defaults_fillspc(menu_ay_registers_overlay_window,1,linea++,textotono);
        sprintf (textotono,"Wrap:           %5d",ql_audio_wrap);
        zxvision_print_string_defaults_fillspc(menu_ay_registers_overlay_window,1,linea++,textotono);
        sprintf (textotono,"Randomness:     %5d",ql_audio_randomness_of_step);
        zxvision_print_string_defaults_fillspc(menu_ay_registers_overlay_window,1,linea++,textotono);
        sprintf (textotono,"Fuziness:       %5d",ql_audio_fuziness);
        zxvision_print_string_defaults_fillspc(menu_ay_registers_overlay_window,1,linea++,textotono);

    }

	else {

	for (chip=0;chip<total_chips;chip++) {


        	vol_A[chip]=ay_3_8912_registros[chip][8] & 15;
        	vol_B[chip]=ay_3_8912_registros[chip][9] & 15;
        	vol_C[chip]=ay_3_8912_registros[chip][10] & 15;

			//Controlar limites, dado que las variables entran sin inicializar
			if (ayregisters_previo_valor_volume_A[chip]>16) ayregisters_previo_valor_volume_A[chip]=16;
			if (ayregisters_previo_valor_volume_B[chip]>16) ayregisters_previo_valor_volume_B[chip]=16;
			if (ayregisters_previo_valor_volume_C[chip]>16) ayregisters_previo_valor_volume_C[chip]=16;


			ayregisters_previo_valor_volume_A[chip]=menu_decae_ajusta_valor_volumen(ayregisters_previo_valor_volume_A[chip],vol_A[chip]);
			ayregisters_previo_valor_volume_B[chip]=menu_decae_ajusta_valor_volumen(ayregisters_previo_valor_volume_B[chip],vol_B[chip]);
			ayregisters_previo_valor_volume_C[chip]=menu_decae_ajusta_valor_volumen(ayregisters_previo_valor_volume_C[chip],vol_C[chip]);



        		//if (ayregisters_previo_valor_volume_A[chip]<vol_A[chip]) ayregisters_previo_valor_volume_A[chip]=vol_A[chip];
        		//if (ayregisters_previo_valor_volume_B[chip]<vol_B[chip]) ayregisters_previo_valor_volume_B[chip]=vol_B[chip];
        		//if (ayregisters_previo_valor_volume_C[chip]<vol_C[chip]) ayregisters_previo_valor_volume_C[chip]=vol_C[chip];


			menu_string_volumen(volumen,ay_3_8912_registros[chip][8],ayregisters_previo_valor_volume_A[chip]);
			sprintf (textovolumen,"Volume A: %s",volumen);
			//menu_escribe_linea_opcion(linea++,-1,1,textovolumen);
			zxvision_print_string_defaults(menu_ay_registers_overlay_window,1,linea++,textovolumen);

			menu_string_volumen(volumen,ay_3_8912_registros[chip][9],ayregisters_previo_valor_volume_B[chip]);
			sprintf (textovolumen,"Volume B: %s",volumen);
			//menu_escribe_linea_opcion(linea++,-1,1,textovolumen);
			zxvision_print_string_defaults(menu_ay_registers_overlay_window,1,linea++,textovolumen);

			menu_string_volumen(volumen,ay_3_8912_registros[chip][10],ayregisters_previo_valor_volume_C[chip]);
			sprintf (textovolumen,"Volume C: %s",volumen);
			//menu_escribe_linea_opcion(linea++,-1,1,textovolumen);
			zxvision_print_string_defaults(menu_ay_registers_overlay_window,1,linea++,textovolumen);



			int freq_a=ay_retorna_frecuencia(0,chip);
			int freq_b=ay_retorna_frecuencia(1,chip);
			int freq_c=ay_retorna_frecuencia(2,chip);
			sprintf (textotono,"Channel A:  %3s %7d Hz",get_note_name(freq_a),freq_a);
			//menu_escribe_linea_opcion(linea++,-1,1,textotono);
			zxvision_print_string_defaults(menu_ay_registers_overlay_window,1,linea++,textotono);

			sprintf (textotono,"Channel B:  %3s %7d Hz",get_note_name(freq_b),freq_b);
			//menu_escribe_linea_opcion(linea++,-1,1,textotono);
			zxvision_print_string_defaults(menu_ay_registers_overlay_window,1,linea++,textotono);

			sprintf (textotono,"Channel C:  %3s %7d Hz",get_note_name(freq_c),freq_c);
			//menu_escribe_linea_opcion(linea++,-1,1,textotono);
			zxvision_print_string_defaults(menu_ay_registers_overlay_window,1,linea++,textotono);


			//Si hay 3 canales, los 3 siguientes items no se ven
			if (total_chips<3) {

			                        //Frecuencia ruido
                        int freq_temp=ay_3_8912_registros[chip][6] & 31;
                        //printf ("Valor registros ruido : %d Hz\n",freq_temp);
                        freq_temp=freq_temp*16;

                        //controlamos divisiones por cero
                        if (!freq_temp) freq_temp++;

                        int freq_ruido=FRECUENCIA_NOISE/freq_temp;

                        sprintf (textotono,"Frequency Noise: %6d Hz",freq_ruido);
                        //menu_escribe_linea_opcion(linea++,-1,1,textotono);
						zxvision_print_string_defaults(menu_ay_registers_overlay_window,1,linea++,textotono);


			//Envelope

                        freq_temp=ay_3_8912_registros[chip][11]+256*(ay_3_8912_registros[chip][12] & 0xFF);


                        //controlamos divisiones por cero
                        if (!freq_temp) freq_temp++;
                        int freq_envelope=FRECUENCIA_ENVELOPE/freq_temp;

                        //sprintf (textotono,"Freq Envelope(*10): %5d Hz",freq_envelope);

			int freq_env_10=freq_envelope/10;
			int freq_env_decimal=freq_envelope-(freq_env_10*10);

			sprintf (textotono,"Freq Envelope:   %4d.%1d Hz",freq_env_10,freq_env_decimal);
      		//menu_escribe_linea_opcion(linea++,-1,1,textotono);
			zxvision_print_string_defaults(menu_ay_registers_overlay_window,1,linea++,textotono);



			char envelope_name[32];
			z80_byte env_type=ay_3_8912_registros[chip][13] & 0x0F;
			return_envelope_name(env_type,envelope_name);
			sprintf (textotono,"Env.: %2d (%s)",env_type,envelope_name);
            //menu_escribe_linea_opcion(linea++,-1,1,textotono);
			zxvision_print_string_defaults(menu_ay_registers_overlay_window,1,linea++,textotono);


			}



			sprintf (textotono,"Tone Channels:  %c %c %c",
				( (ay_3_8912_registros[chip][7]&1)==0 ? 'A' : ' '),
				( (ay_3_8912_registros[chip][7]&2)==0 ? 'B' : ' '),
				( (ay_3_8912_registros[chip][7]&4)==0 ? 'C' : ' '));
			//menu_escribe_linea_opcion(linea++,-1,1,textotono);
			zxvision_print_string_defaults(menu_ay_registers_overlay_window,1,linea++,textotono);

			//Si hay 3 canales, los 3 siguientes items no se ven
			if (total_chips<3) {

                        sprintf (textotono,"Noise Channels: %c %c %c",
                                ( (ay_3_8912_registros[chip][7]&8)==0  ? 'A' : ' '),
                                ( (ay_3_8912_registros[chip][7]&16)==0 ? 'B' : ' '),
                                ( (ay_3_8912_registros[chip][7]&32)==0 ? 'C' : ' '));
                        //menu_escribe_linea_opcion(linea++,-1,1,textotono);
						zxvision_print_string_defaults(menu_ay_registers_overlay_window,1,linea++,textotono);
			}

            //Contadores graficos
            //if (chip==0) {
                int margen_horizontal=30;

                int longitud_linea=ZXVISION_WIDGET_TYPE_SPEEDOMETER_LINE_LENGTH;

                //fila inicial segun total de chips
                int fila_texto=12;
                if (total_chips==2) fila_texto=22;
                if (total_chips==3) fila_texto=22;

                //Y desplazamiento vertical segun donde ubicamos el contador grafico segun el contador de chip
                fila_texto +=((longitud_linea/8)+4)*chip;

               //TODO: con 3 chips no cabe en vertical todo
                int yorigen_linea=(fila_texto*menu_char_height)+longitud_linea+menu_char_height*2-1;
                //-1 porque la linea del speedometer cuando esta horizontal
                //del todo, queda en la siguiente posicion y

                char buffer_texto_meter[100];


                //Volumen A
                int columna_texto=1;
                int xorigen_linea=40;
                int porcentaje; //=(valor_volumen*100)/15;
                //porcentaje=((ay_3_8912_registros[chip][8]&15)*100)/15;

                char sensor_name[SENSORS_MAX_SHORT_NAME];
                sprintf(sensor_name,"ay_vol_chip%d_chan_A",chip);
                porcentaje=sensor_get_percentaje_value(sensor_name);

                sprintf(buffer_texto_meter,"A[%d]",chip);
                int color=ESTILO_GUI_COLOR_WAVEFORM;
                if (porcentaje>=85) color=ESTILO_GUI_COLOR_AVISO;

                zxvision_print_string_defaults(menu_ay_registers_overlay_window,columna_texto,fila_texto,buffer_texto_meter);
                //borrar primero el espacio ocupado por el speedometer por el frame anterior. Si no lo hicieramos, quedaria "rastro"
                zxvision_widgets_erase_speedometer(menu_ay_registers_overlay_window,xorigen_linea,yorigen_linea);
                zxvision_widgets_draw_speedometer_common(menu_ay_registers_overlay_window,xorigen_linea,yorigen_linea,
                    porcentaje,color,color);

                //Volumen B
                columna_texto += (longitud_linea*2+margen_horizontal)/menu_char_width;
                xorigen_linea=xorigen_linea+longitud_linea*2+margen_horizontal; //A la derecha del anterior
                //porcentaje=((ay_3_8912_registros[chip][9]&15)*100)/15;

                sprintf(sensor_name,"ay_vol_chip%d_chan_B",chip);
                porcentaje=sensor_get_percentaje_value(sensor_name);

                sprintf(buffer_texto_meter,"B[%d]",chip);
                color=ESTILO_GUI_COLOR_WAVEFORM;
                if (porcentaje>=85) color=ESTILO_GUI_COLOR_AVISO;

                zxvision_print_string_defaults(menu_ay_registers_overlay_window,columna_texto,fila_texto,buffer_texto_meter);
                zxvision_widgets_erase_speedometer(menu_ay_registers_overlay_window,xorigen_linea,yorigen_linea);
                zxvision_widgets_draw_speedometer_common(menu_ay_registers_overlay_window,xorigen_linea,yorigen_linea,
                    porcentaje,color,color);

                //Volumen C
                columna_texto += (longitud_linea*2+margen_horizontal)/menu_char_width;
                xorigen_linea=xorigen_linea+longitud_linea*2+margen_horizontal; //A la derecha del anterior
                //porcentaje=((ay_3_8912_registros[chip][10]&15)*100)/15;

                sprintf(sensor_name,"ay_vol_chip%d_chan_C",chip);
                porcentaje=sensor_get_percentaje_value(sensor_name);

                sprintf(buffer_texto_meter,"C[%d]",chip);
                color=ESTILO_GUI_COLOR_WAVEFORM;
                if (porcentaje>=85) color=ESTILO_GUI_COLOR_AVISO;

                zxvision_print_string_defaults(menu_ay_registers_overlay_window,columna_texto,fila_texto,buffer_texto_meter);
                zxvision_widgets_erase_speedometer(menu_ay_registers_overlay_window,xorigen_linea,yorigen_linea);
                zxvision_widgets_draw_speedometer_common(menu_ay_registers_overlay_window,xorigen_linea,yorigen_linea,
                    porcentaje,color,color);
            //}

	}

	}




	//Hacer decaer volumenes si multitarea off
                        //Decrementar volumenes que caen, pero hacerlo no siempre, sino 2 veces por segundo
            //esto hara ejecutar esto 2 veces por segundo
            if ( ((contador_segundo%500) == 0 && menu_ayregisters_valor_contador_segundo_anterior!=contador_segundo) || menu_multitarea==0) {

                                 menu_ayregisters_valor_contador_segundo_anterior=contador_segundo;
                                //printf ("Refrescando. contador_segundo=%d. chip: %d\n",contador_segundo,chip);

				for (chip=0;chip<total_chips;chip++) {

					ayregisters_previo_valor_volume_A[chip]=menu_decae_dec_valor_volumen(ayregisters_previo_valor_volume_A[chip],vol_A[chip]);
					ayregisters_previo_valor_volume_B[chip]=menu_decae_dec_valor_volumen(ayregisters_previo_valor_volume_B[chip],vol_B[chip]);
					ayregisters_previo_valor_volume_C[chip]=menu_decae_dec_valor_volumen(ayregisters_previo_valor_volume_C[chip],vol_C[chip]);


				}


				ayregisters_previo_valor_volume_noise=menu_decae_dec_valor_volumen(ayregisters_previo_valor_volume_noise,vol_noise);


        }


	zxvision_draw_window_contents(menu_ay_registers_overlay_window);


}


//Ventana como variable global
zxvision_window zxvision_ay_registers_overlay;


void menu_ay_registers_crea_ventana(zxvision_window *ventana,int xventana,int yventana,int ancho_ventana,int alto_ventana,
    int is_minimized,int is_maximized,int ancho_antes_minimize,int alto_antes_minimize)
{
		//zxvision_new_window(ventana,xventana,yventana,ancho_ventana,alto_ventana,ancho_ventana-1,alto_ventana-2,"Audio Chip Registers");

        zxvision_new_window_gn_cim(ventana,xventana,yventana,ancho_ventana,alto_ventana,ancho_ventana-1,alto_ventana-2,"Audio Chip Registers",
            "ayregisters",is_minimized,is_maximized,ancho_antes_minimize,alto_antes_minimize);
		ventana->can_be_backgrounded=1;
		//indicar nombre del grabado de geometria
		//strcpy(ventana->geometry_name,"ayregisters");
        //restaurar estado minimizado de ventana
        //ventana->is_minimized=is_minimized;
}

void menu_ay_registers(MENU_ITEM_PARAMETERS)
{
    menu_espera_no_tecla();
	menu_reset_counters_tecla_repeticion();

		if (!menu_multitarea) {
			menu_warn_message("This window needs multitask enabled");
			return;
		}

		zxvision_window *ventana;
		ventana=&zxvision_ay_registers_overlay;

    //IMPORTANTE! no crear ventana si ya existe. Esto hay que hacerlo en todas las ventanas que permiten background.
    //si no se hiciera, se crearia la misma ventana, y en la lista de ventanas activas , al redibujarse,
    //la primera ventana repetida apuntaria a la segunda, que es el mismo puntero, y redibujaria la misma, y se quedaria en bucle colgado
	//zxvision_delete_window_if_exists(ventana);


    int total_chips=audio_get_total_chips();
    if (total_chips>3) total_chips=3;

    //Crear ventana si no existe
    if (!zxvision_if_window_already_exists(ventana)) {

        int xventana,yventana,ancho_ventana,alto_ventana,is_minimized,is_maximized,ancho_antes_minimize,alto_antes_minimize;

        if (!util_find_window_geometry("ayregisters",&xventana,&yventana,&ancho_ventana,&alto_ventana,&is_minimized,&is_maximized,&ancho_antes_minimize,&alto_antes_minimize)) {

            if (total_chips==1) {
                //yventana=5;
            }
            else {
                //yventana=0;
            }

            //xventana=menu_origin_x()+1;
            ancho_ventana=30;

            //El alto lo cambiamos segun el numero de chips
            if (total_chips==1) {
                    alto_ventana=14;
            }
            else {
                    alto_ventana=24;
            }

            xventana=menu_center_x()-ancho_ventana/2;
            yventana=menu_center_y()-alto_ventana/2;

        }



        //Para poder controlar redimensionamientos de ventana y recrearla de nuevo
        //No es necesario, pero es mas bonito... asi se recrea la ventana, si era muy pequeña, hacerla mas grande
        //garantiza que se podra leer todo el texto
        //int alto_anterior=alto_ventana;
        //int ancho_anterior=ancho_ventana;


        menu_ay_registers_crea_ventana(ventana,xventana,yventana,ancho_ventana,alto_ventana,is_minimized,is_maximized,ancho_antes_minimize,alto_antes_minimize);

    }
    //Si ya existe, activar esta ventana
    else {
        zxvision_activate_this_window(ventana);
    }

    int alto_anterior;
    int ancho_anterior;

    zxvision_window_save_size(ventana,&ancho_anterior,&alto_anterior);

    zxvision_draw_window(ventana);


    //Cambiamos funcion overlay de texto de menu
    //Se establece a la de funcion de onda + texto
    //cambio overlay
    zxvision_set_window_overlay(ventana,menu_ay_registers_overlay);

    menu_ay_registers_overlay_window=ventana; //Decimos que el overlay lo hace sobre la ventana que tenemos aqui


	//Toda ventana que este listada en zxvision_known_window_names_array debe permitir poder salir desde aqui
	//Se sale despues de haber inicializado overlay y de cualquier otra variable que necesite el overlay
	if (zxvision_currently_restoring_windows_on_start) {
		//printf ("Saliendo de ventana ya que la estamos restaurando en startup\n");
		return;
	}


	z80_byte tecla;

	do {
		tecla=zxvision_common_getkey_refresh();
		zxvision_handle_cursors_pgupdn(ventana,tecla);
		//printf ("tecla: %d\n",tecla);

		//Si ha cambiado el tamaño
        //Ya NO hace falta esto, pues zxvision ya recrea la ventana al ampliarla
        /*
		alto_ventana=ventana->visible_height;
		ancho_ventana=ventana->visible_width;
		xventana=ventana->x;
		yventana=ventana->y;
		if (alto_ventana!=alto_anterior || ancho_ventana!=ancho_anterior) {
			//printf ("recrear ventana ay registers\n");
			//Recrear ventana
            int is_minimized=ventana->is_minimized;

			zxvision_destroy_window(ventana);
			//alto_anterior=alto_ventana;
			//ancho_anterior=ancho_ventana;
			menu_ay_registers_crea_ventana(ventana,xventana,yventana,ancho_ventana,alto_ventana,is_minimized,ancho_antes_minimize,alto_antes_minimize);
			zxvision_window_save_size(ventana,&ancho_anterior,&alto_anterior);
            zxvision_draw_window(ventana);

            //Indicar tamanyo de antes minimizado, que es el que tenia al inicio
            //dado que se recrea la ventana siempre que cambia tamaño (y si se minimiza tambien),
            //queremos que se indique el tamaño que tenia antes de minimizar por si se deshace el minimizado
            //ventana->height_before_max_min_imize=alto_ventana_inicial;
            //ventana->width_before_max_min_imize=ancho_ventana_inicial;
            //printf ("despues ventana ay registers\n");
		}
        */


	} while (tecla!=2 && tecla!=3);

	//Gestionar salir con tecla background

	menu_espera_no_tecla(); //Si no, se va al menu anterior.
	//En AY Piano por ejemplo esto no pasa aunque el estilo del menu es el mismo...




	util_add_window_geometry_compact(ventana);


	if (tecla==3) {
		zxvision_message_put_window_background();
	}

	else {

		zxvision_destroy_window(ventana);
 	}
}





int menu_debug_tsconf_tbblue_msx_videoregisters_valor_contador_segundo_anterior;

zxvision_window *menu_debug_tsconf_tbblue_msx_videoregisters_overlay_window;

void menu_debug_tsconf_tbblue_msx_videoregisters_overlay(void)
{


    //si ventana minimizada, no ejecutar todo el codigo de overlay
    if (menu_debug_tsconf_tbblue_msx_videoregisters_overlay_window->is_minimized) return;

    //printf("overlay video info %d\n",contador_segundo);


	zxvision_window *ventana;

	ventana=menu_debug_tsconf_tbblue_msx_videoregisters_overlay_window;

    //esto hara ejecutar esto 2 veces por segundo
    if ( ((contador_segundo%500) == 0 && menu_debug_tsconf_tbblue_msx_videoregisters_valor_contador_segundo_anterior!=contador_segundo) || menu_multitarea==0) {
        menu_debug_tsconf_tbblue_msx_videoregisters_valor_contador_segundo_anterior=contador_segundo;
        //printf ("Refrescando. contador_segundo=%d\n",contador_segundo);

        int linea=0;
        //int opcode;
        //int sumatotal;


        char texto_buffer[64];

        char texto_buffer2[64];

        //Empezar con espacio
        texto_buffer[0]=' ';


        if (MACHINE_IS_TSCONF) {

            int vpage_addr=tsconf_get_vram_page()*16384;

            tsconf_get_current_video_mode(texto_buffer2);
            sprintf (texto_buffer,"Video mode: %s",texto_buffer2);
            //menu_escribe_linea_opcion(linea++,-1,1,texto_buffer);
            //zxvision_print_string_defaults(ventana,1,linea++,texto_buffer);

            //Con fillspc porque interesa borrar "restos" de modos de video anterior
            zxvision_print_string_defaults_fillspc(ventana,1,linea++,texto_buffer);

            //printf ("[%s] [%s]\n",texto_buffer,texto_buffer2);

            sprintf (texto_buffer,"Video addr: %06XH",vpage_addr);
            //menu_escribe_linea_opcion(linea++,-1,1,texto_buffer);
            zxvision_print_string_defaults(ventana,1,linea++,texto_buffer);

            sprintf (texto_buffer,"Tile Map Page: %06XH",tsconf_return_tilemappage() );
            //menu_escribe_linea_opcion(linea++,-1,1,texto_buffer);
            zxvision_print_string_defaults(ventana,1,linea++,texto_buffer);

            sprintf (texto_buffer,"Tile 0 Graphics addr: %06XH",tsconf_return_tilegraphicspage(0) );
            //menu_escribe_linea_opcion(linea++,-1,1,texto_buffer);
            zxvision_print_string_defaults(ventana,1,linea++,texto_buffer);

            sprintf (texto_buffer,"Tile 1 Graphics addr: %06XH",tsconf_return_tilegraphicspage(1) );
            //menu_escribe_linea_opcion(linea++,-1,1,texto_buffer);
            zxvision_print_string_defaults(ventana,1,linea++,texto_buffer);


            sprintf (texto_buffer,"Sprite Graphics addr: %06XH",tsconf_return_spritesgraphicspage() );
            //menu_escribe_linea_opcion(linea++,-1,1,texto_buffer);
            zxvision_print_string_defaults(ventana,1,linea++,texto_buffer);

        }

        if (MACHINE_IS_TBBLUE) {

            //menu_escribe_linea_opcion(linea++,-1,1,"ULA Video mode:");
            zxvision_print_string_defaults(ventana,1,linea++,"ULA Video mode:");

            //Con fillspc porque interesa borrar "restos" de modos de video anterior
            zxvision_print_string_defaults_fillspc(ventana,1,linea++,get_spectrum_ula_string_video_mode() );

            linea++;

            //menu_escribe_linea_opcion(linea++,-1,1,"Palette format:");
            //zxvision_print_string_defaults(&ventana,1,linea++,"Palette:");

            tbblue_get_string_palette_format(texto_buffer2);
            sprintf (texto_buffer,"Palette: %s",texto_buffer2);

            zxvision_print_string_defaults(ventana,1,linea++,texto_buffer);

            linea++;

            /*
            (R/W) 0x12 (18) => Layer 2 RAM page
bits 7-6 = Reserved, must be 0
bits 5-0 = SRAM page (point to page 8 after a Reset)

(R/W) 0x13 (19) => Layer 2 RAM shadow page
bits 7-6 = Reserved, must be 0
bits 5-0 = SRAM page (point to page 11 after a Reset)
            */

        //tbblue_get_offset_start_layer2_reg

            sprintf (texto_buffer,"Layer 2 mode:     %s",tbblue_get_layer2_mode_name() );
            zxvision_print_string_defaults(ventana,1,linea++,texto_buffer);

            sprintf (texto_buffer,"Layer 2 addr:          %06XH",tbblue_get_offset_start_layer2_reg(tbblue_registers[18]) );
            zxvision_print_string_defaults(ventana,1,linea++,texto_buffer);

            sprintf (texto_buffer,"Layer 2 shadow addr:   %06XH",tbblue_get_offset_start_layer2_reg(tbblue_registers[19]) );
            zxvision_print_string_defaults(ventana,1,linea++,texto_buffer);

            sprintf (texto_buffer,"Tilemap base addr: RAM%d:%02X00H",tbblue_get_ram_page_tilemap(),tbblue_get_offset_start_tilemap());
            zxvision_print_string_defaults(ventana,1,linea++,texto_buffer);

            sprintf (texto_buffer,"Tile defin.  addr: RAM%d:%02X00H",tbblue_get_ram_page_tiledef(),tbblue_get_offset_start_tiledef() );
            zxvision_print_string_defaults(ventana,1,linea++,texto_buffer);

            sprintf (texto_buffer,"Tile width: %d columns",tbblue_get_tilemap_width() );
            zxvision_print_string_defaults(ventana,1,linea++,texto_buffer);

            sprintf (texto_buffer,"Tile bpp: %d", (tbblue_tiles_are_monocrome() ? 1 : 4)  );
            zxvision_print_string_defaults(ventana,1,linea++,texto_buffer);

            /*
            z80_byte tbblue_clip_windows[TBBLUE_CLIP_WINDOW_LAYER2][4];
z80_byte tbblue_clip_windows[TBBLUE_CLIP_WINDOW_SPRITES][4];
z80_byte tbblue_clip_windows[TBBLUE_CLIP_WINDOW_ULA][4];
z80_byte tbblue_clip_windows[TBBLUE_CLIP_WINDOW_TILEMAP][4];
            */

            linea++;
            sprintf (texto_buffer,"Clip Windows:");
            zxvision_print_string_defaults(ventana,1,linea++,texto_buffer);

            sprintf (texto_buffer,"Layer2:  X=%3d-%3d Y=%3d-%3d",
            tbblue_clip_windows[TBBLUE_CLIP_WINDOW_LAYER2][0],tbblue_clip_windows[TBBLUE_CLIP_WINDOW_LAYER2][1],tbblue_clip_windows[TBBLUE_CLIP_WINDOW_LAYER2][2],tbblue_clip_windows[TBBLUE_CLIP_WINDOW_LAYER2][3]);
            zxvision_print_string_defaults(ventana,1,linea++,texto_buffer);


            sprintf (texto_buffer,"Sprites: X=%3d-%3d Y=%3d-%3d",
            tbblue_clip_windows[TBBLUE_CLIP_WINDOW_SPRITES][0],tbblue_clip_windows[TBBLUE_CLIP_WINDOW_SPRITES][1],tbblue_clip_windows[TBBLUE_CLIP_WINDOW_SPRITES][2],tbblue_clip_windows[TBBLUE_CLIP_WINDOW_SPRITES][3]);
            zxvision_print_string_defaults(ventana,1,linea++,texto_buffer);


            sprintf (texto_buffer,"ULA:     X=%3d-%3d Y=%3d-%3d",
            tbblue_clip_windows[TBBLUE_CLIP_WINDOW_ULA][0],tbblue_clip_windows[TBBLUE_CLIP_WINDOW_ULA][1],tbblue_clip_windows[TBBLUE_CLIP_WINDOW_ULA][2],tbblue_clip_windows[TBBLUE_CLIP_WINDOW_ULA][3]);
            zxvision_print_string_defaults(ventana,1,linea++,texto_buffer);


            sprintf (texto_buffer,"Tilemap: X=%3d-%3d Y=%3d-%3d",
            tbblue_clip_windows[TBBLUE_CLIP_WINDOW_TILEMAP][0]*2,tbblue_clip_windows[TBBLUE_CLIP_WINDOW_TILEMAP][1]*2+1,tbblue_clip_windows[TBBLUE_CLIP_WINDOW_TILEMAP][2],tbblue_clip_windows[TBBLUE_CLIP_WINDOW_TILEMAP][3]);
            zxvision_print_string_defaults(ventana,1,linea++,texto_buffer);



            linea++;
            sprintf (texto_buffer,"Offset Windows:");
            //menu_escribe_linea_opcion(linea++,-1,1,texto_buffer);
            zxvision_print_string_defaults(ventana,1,linea++,texto_buffer);


            int off_layer2_x=tbblue_registers[22] + (tbblue_registers[113]&1)*256;
            sprintf (texto_buffer,"Layer2:  X=%4d  Y=%3d",off_layer2_x,tbblue_registers[23]);
            zxvision_print_string_defaults(ventana,1,linea++,texto_buffer);


            sprintf (texto_buffer,"LoRes:   X=%4d  Y=%3d",tbblue_registers[50],tbblue_registers[51]);
            zxvision_print_string_defaults(ventana,1,linea++,texto_buffer);

            sprintf (texto_buffer,"ULA:     X=%4d  Y=%3d",tbblue_registers[38],tbblue_registers[39]);
            zxvision_print_string_defaults(ventana,1,linea++,texto_buffer);


            //Offset X puede llegar hasta 1023. Por tanto 4 cifras. El resto X solo 3 cifras, pero los dejamos a 4 para que formato quede igual en pantalla
            sprintf (texto_buffer,"Tilemap: X=%4d  Y=%3d",tbblue_registers[48]+256*(tbblue_registers[47]&3),tbblue_registers[49]);
            zxvision_print_string_defaults(ventana,1,linea++,texto_buffer);

        }

        if (MACHINE_HAS_VDP_9918A) {

            sprintf (texto_buffer,"Video mode:%s",get_vdp_9918_string_video_mode() );
            zxvision_print_string_defaults_fillspc(ventana,1,linea++,texto_buffer);


            sprintf (texto_buffer,"Background Color: %2d",vdp_9918a_get_border_color());
            zxvision_print_string_defaults(ventana,1,linea++,texto_buffer);

            sprintf (texto_buffer,"Foreground Color: %2d",vdp_9918a_get_foreground_color());
            zxvision_print_string_defaults(ventana,1,linea++,texto_buffer);


            if (vdp_9918a_si_sms_video_mode4()) {
                sprintf (texto_buffer,"Sprite size: 8X%d",vdp_9918a_sms_get_sprite_height());
                zxvision_print_string_defaults_fillspc(ventana,1,linea++,texto_buffer);
            }
            else {
                int sprite_size=vdp_9918a_get_sprite_size();
                sprintf (texto_buffer,"Sprite size: %dX%d",sprite_size,sprite_size);
                zxvision_print_string_defaults_fillspc(ventana,1,linea++,texto_buffer);
            }

            sprintf (texto_buffer,"Magnification: %dX",vdp_9918a_get_sprite_double());
            zxvision_print_string_defaults(ventana,1,linea++,texto_buffer);

            sprintf (texto_buffer,"Name Table:           %04XH",vdp_9918a_get_pattern_name_table());
            zxvision_print_string_defaults(ventana,1,linea++,texto_buffer);

            sprintf (texto_buffer,"Color Table:          %04XH",vdp_9918a_get_pattern_color_table());
            zxvision_print_string_defaults(ventana,1,linea++,texto_buffer);

            sprintf (texto_buffer,"Pattern Generator:    %04XH",vdp_9918a_get_pattern_base_address());
            zxvision_print_string_defaults(ventana,1,linea++,texto_buffer);


            sprintf (texto_buffer,"Sprite Attr. Table:   %04XH",vdp_9918a_get_sprite_attribute_table());
            zxvision_print_string_defaults(ventana,1,linea++,texto_buffer);

            sprintf (texto_buffer,"Sprite Pattern Table: %04XH",vdp_9918a_get_sprite_pattern_table());
            zxvision_print_string_defaults(ventana,1,linea++,texto_buffer);

            if (vdp_9918a_si_sms_video_mode4()) {
                sprintf (texto_buffer,"Scroll Horizontal:    %d",vdp_9918a_sms_get_scroll_horizontal());
                zxvision_print_string_defaults_fillspc(ventana,1,linea++,texto_buffer);

                sprintf (texto_buffer,"Scroll Vertical:      %d",vdp_9918a_sms_get_scroll_vertical());
                zxvision_print_string_defaults_fillspc(ventana,1,linea++,texto_buffer);
            }



        }

        if (MACHINE_IS_CPC) {
            z80_byte video_mode=cpc_gate_registers[2] &3;
            sprintf(texto_buffer,"Video mode: %d %s",video_mode,cpc_video_modes_strings[video_mode]);
            zxvision_print_string_defaults_fillspc(ventana,1,linea++,texto_buffer);

            sprintf(texto_buffer,"Full size:       %d X %d",
                cpc_crtc_get_total_horizontal(),cpc_crtc_get_total_vertical()
            );
            zxvision_print_string_defaults_fillspc(ventana,1,linea++,texto_buffer);

            sprintf(texto_buffer,"Pixel zone size: %d X %d",
                cpc_crtc_get_total_pixels_horizontal(),
                cpc_crtc_get_total_pixels_vertical()
            );
            zxvision_print_string_defaults_fillspc(ventana,1,linea++,texto_buffer);

            sprintf(texto_buffer,"Videoram offset: %03XH",cpc_ctrc_get_offset_videoram());
            zxvision_print_string_defaults_fillspc(ventana,1,linea++,texto_buffer);

            sprintf(texto_buffer,"Videoram page:   %XH",cpc_ctrc_get_video_page());
            zxvision_print_string_defaults_fillspc(ventana,1,linea++,texto_buffer);

            //zxvision_print_string_defaults_fillspc(ventana,1,linea++,"Borders");

            sprintf(texto_buffer,"Border Top:      %3d Bottom: %3d",
                cpc_crtc_get_top_border_height(),
                cpc_crtc_get_bottom_border_height()
            );
            zxvision_print_string_defaults_fillspc(ventana,1,linea++,texto_buffer);

            sprintf(texto_buffer,"Border Left:     %3d Right:  %3d",
                cpc_crtc_get_total_left_border(),
                cpc_crtc_get_total_right_border()
            );
            zxvision_print_string_defaults_fillspc(ventana,1,linea++,texto_buffer);

            //zxvision_print_string_defaults_fillspc(ventana,1,linea++,"");

            sprintf(texto_buffer,"Hsync width:     %d",cpc_crtc_get_total_hsync_width());
            zxvision_print_string_defaults_fillspc(ventana,1,linea++,texto_buffer);

            sprintf(texto_buffer,"Vsync height:    %d",cpc_crtc_get_total_vsync_height_crtc());
            zxvision_print_string_defaults_fillspc(ventana,1,linea++,texto_buffer);

        }


        zxvision_draw_window_contents(ventana);
    }
}



zxvision_window menu_debug_tsconf_tbblue_msx_videoregisters_ventana;


void menu_debug_tsconf_tbblue_msx_videoregisters(MENU_ITEM_PARAMETERS)
{

	menu_espera_no_tecla();
	menu_reset_counters_tecla_repeticion();

	zxvision_window *ventana;

	ventana=&menu_debug_tsconf_tbblue_msx_videoregisters_ventana;

	//IMPORTANTE! no crear ventana si ya existe. Esto hay que hacerlo en todas las ventanas que permiten background.
	//si no se hiciera, se crearia la misma ventana, y en la lista de ventanas activas , al redibujarse,
	//la primera ventana repetida apuntaria a la segunda, que es el mismo puntero, y redibujaria la misma, y se quedaria en bucle colgado
	//zxvision_delete_window_if_exists(ventana);

    //Crear ventana si no existe
    if (!zxvision_if_window_already_exists(ventana)) {

        int xventana,yventana,ancho_ventana,alto_ventana,is_minimized,is_maximized,ancho_antes_minimize,alto_antes_minimize;

        if (!util_find_window_geometry("videoinfo",&xventana,&yventana,&ancho_ventana,&alto_ventana,&is_minimized,&is_maximized,&ancho_antes_minimize,&alto_antes_minimize)) {

            ancho_ventana=32;
            xventana=menu_center_x()-ancho_ventana/2;

            if (MACHINE_IS_TBBLUE) {
                alto_ventana=24;
            }

            else if (MACHINE_HAS_VDP_9918A) {
                alto_ventana=12;
            }

            else if (MACHINE_IS_CPC) {
                alto_ventana=9;
            }

            else {
                //yventana=7;
                alto_ventana=8;
            }

            yventana=menu_center_y()-alto_ventana/2;

        }




        zxvision_new_window_gn_cim(ventana,xventana,yventana,ancho_ventana,alto_ventana,ancho_ventana-1,alto_ventana-2,"Video Info",
            "videoinfo",is_minimized,is_maximized,ancho_antes_minimize,alto_antes_minimize);

        ventana->can_be_backgrounded=1;
        //indicar nombre del grabado de geometria
        //strcpy(ventana->geometry_name,"videoinfo");
        //restaurar estado minimizado de ventana
        //ventana->is_minimized=is_minimized;

    }

    //Si ya existe, activar esta ventana
    else {

        zxvision_activate_this_window(ventana);
    }

	zxvision_draw_window(ventana);


	menu_debug_tsconf_tbblue_msx_videoregisters_overlay_window=ventana;



	//Cambiamos funcion overlay de texto de menu
	//Se establece a la de funcion de onda + texto
    //cambio overlay
    zxvision_set_window_overlay(ventana,menu_debug_tsconf_tbblue_msx_videoregisters_overlay);


    //Toda ventana que este listada en zxvision_known_window_names_array debe permitir poder salir desde aqui
    //Se sale despues de haber inicializado overlay y de cualquier otra variable que necesite el overlay
    if (zxvision_currently_restoring_windows_on_start) {
        //printf ("Saliendo de ventana ya que la estamos restaurando en startup\n");
        return;
    }

	z80_byte tecla;

	do {
		tecla=zxvision_common_getkey_refresh();
		zxvision_handle_cursors_pgupdn(ventana,tecla);
		//printf ("tecla: %d\n",tecla);
	} while (tecla!=2 && tecla!=3);

	//Gestionar salir con tecla background

	menu_espera_no_tecla(); //Si no, se va al menu anterior.
	//En AY Piano por ejemplo esto no pasa aunque el estilo del menu es el mismo...




	util_add_window_geometry_compact(ventana);

	if (tecla==3) {
		zxvision_message_put_window_background();
	}

	else {


		zxvision_destroy_window(ventana);

	}

}


#define TSCONF_SPRITENAV_WINDOW_ANCHO 32
#define TSCONF_SPRITENAV_WINDOW_ALTO 20
#define TSCONF_SPRITENAV_WINDOW_X (menu_center_x()-TSCONF_SPRITENAV_WINDOW_ANCHO/2 )
#define TSCONF_SPRITENAV_WINDOW_Y (menu_center_y()-TSCONF_SPRITENAV_WINDOW_ALTO/2)




//int menu_debug_tsconf_tbblue_msx_spritenav_current_palette=0;
int menu_debug_tsconf_tbblue_msx_spritenav_current_sprite=0;


zxvision_window *menu_debug_tsconf_tbblue_msx_spritenav_draw_sprites_window;

int menu_debug_tsconf_tbblue_msx_spritenav_get_total_sprites(void)
{
	int limite;

	limite=TSCONF_MAX_SPRITES; //85 sprites max

	if (MACHINE_IS_TBBLUE) limite=TBBLUE_MAX_SPRITES;
	else if (MACHINE_HAS_VDP_9918A) {
        if (vdp_9918a_si_sms_video_mode4() ) {
            limite=VDP_9918A_SMS_MODE4_MAX_SPRITES;
        }
        else limite=VDP_9918A_MAX_SPRITES;
    }

	return limite;
}

z80_bit menu_debug_spritenav_raw={0};

int menu_debug_tsconf_tbblue_msx_spritenav_get_total_height_win(void)
{

	//menu_debug_spritenav_raw.v

	int multiplicador;


	if (MACHINE_IS_TSCONF) multiplicador=2;


	else if (MACHINE_HAS_VDP_9918A) multiplicador=2;


	else multiplicador=3;


	if (menu_debug_spritenav_raw.v) multiplicador=1;

//+1 por la linea de leyenda
	return menu_debug_tsconf_tbblue_msx_spritenav_get_total_sprites()*multiplicador+1;

}




//Muestra lista de sprites
void menu_debug_tsconf_tbblue_msx_spritenav_lista_sprites(void)
{

	char dumpmemoria[64];

	int linea_color;
	int limite;

	int linea=0;


	limite=menu_debug_tsconf_tbblue_msx_spritenav_get_total_sprites();

	//z80_byte tbsprite_sprites[TBBLUE_MAX_SPRITES][4];
	/*
	1st: X position (bits 7-0).
2nd: Y position (0-255).
3rd: bits 7-4 is palette offset, bit 3 is X mirror, bit 2 is Y mirror, bit 1 is the rotate flag and bit 0 is X MSB.
4th: bit 7 is the visible flag, bit 6 is reserved, bits 5-0 is Name (pattern index, 0-63).
*/

	int current_sprite;


    //Forzar a mostrar atajos
    z80_bit antes_menu_writing_inverse_color;
    antes_menu_writing_inverse_color.v=menu_writing_inverse_color.v;
    menu_writing_inverse_color.v=1;

    sprintf(dumpmemoria,"[%c] ~~Raw mode list",(menu_debug_spritenav_raw.v ? 'X' : ' '));
 zxvision_print_string_defaults(menu_debug_tsconf_tbblue_msx_spritenav_draw_sprites_window,1,linea++,dumpmemoria);

    //Restaurar comportamiento atajos
    menu_writing_inverse_color.v=antes_menu_writing_inverse_color.v;




		for (linea_color=0;linea_color<limite;linea_color++) {

			current_sprite=menu_debug_tsconf_tbblue_msx_spritenav_current_sprite+linea_color;

			if (MACHINE_IS_TSCONF) {

				int offset=current_sprite*6;

				if (menu_debug_spritenav_raw.v) {

				int indice_string;

				sprintf (dumpmemoria,"%03d ",current_sprite);

				indice_string=4;

				int i;

				for (i=0;i<6;i++) {
				  sprintf(&dumpmemoria[indice_string],"%02X ",tsconf_fmaps[0x200+offset+i]);
				  indice_string +=3;
				}

				zxvision_print_string_defaults_fillspc(menu_debug_tsconf_tbblue_msx_spritenav_draw_sprites_window,1,linea++,dumpmemoria);

				}

				else {


				z80_byte sprite_r0h=tsconf_fmaps[0x200+offset+1];

				z80_byte sprite_leap=sprite_r0h&64;

				int sprite_act=sprite_r0h&32;
				int y=tsconf_fmaps[0x200+offset]+256*(sprite_r0h&1);
				z80_byte ysize=8*(1+((sprite_r0h>>1)&7));


				z80_byte sprite_r1h=tsconf_fmaps[0x200+offset+3];
				int x=tsconf_fmaps[0x200+offset+2]+256*(sprite_r1h&1);
				z80_byte xsize=8*(1+((sprite_r1h>>1)&7));

				z80_byte sprite_r2h=tsconf_fmaps[0x200+offset+5];
				z80_int tnum=(tsconf_fmaps[0x200+offset+4])+256*(sprite_r2h&15);
						//Tile Number for upper left corner. Bits 0-5 are X Position in Graphics Bitmap, bits 6-11 - Y Position.
				z80_int tnum_x=tnum & 63;
				z80_int tnum_y=(tnum>>6)&63;

				z80_byte spal=(sprite_r2h>>4)&15;

				z80_byte sprite_xf=sprite_r1h&128;
				z80_byte sprite_yf=sprite_r0h&128;

				sprintf (dumpmemoria,"%02d X: %3d Y: %3d (%2dX%2d)",current_sprite,x,y,xsize,ysize);
				//menu_escribe_linea_opcion(linea++,-1,1,dumpmemoria);
				zxvision_print_string_defaults(menu_debug_tsconf_tbblue_msx_spritenav_draw_sprites_window,1,linea++,dumpmemoria);

				sprintf (dumpmemoria,"Tile:%2d,%2d %s %s %s %s P:%2d",tnum_x,tnum_y,
					(sprite_act ? "ACT" : "   "),(sprite_leap ? "LEAP": "    "),
					(sprite_xf ? "XF" : "  "),(sprite_yf ? "YF": "  "),
					spal );

				//menu_escribe_linea_opcion(linea++,-1,1,dumpmemoria);
				zxvision_print_string_defaults(menu_debug_tsconf_tbblue_msx_spritenav_draw_sprites_window,1,linea++,dumpmemoria);

				}
			}

			if (MACHINE_IS_TBBLUE) {

				if (menu_debug_spritenav_raw.v) {

				int indice_string;

				sprintf (dumpmemoria,"%03d ",current_sprite);

				indice_string=4;

				int i;

				for (i=0;i<TBBLUE_SPRITE_ATTRIBUTE_SIZE;i++) {
				  sprintf(&dumpmemoria[indice_string],"%02X ",tbsprite_new_sprites[current_sprite][i]);
				  indice_string +=3;
				}

				zxvision_print_string_defaults_fillspc(menu_debug_tsconf_tbblue_msx_spritenav_draw_sprites_window,1,linea++,dumpmemoria);

				}

				else {


					//z80_byte tbsprite_sprites[TBBLUE_MAX_SPRITES][4];
	/*
	1st: X position (bits 7-0).
2nd: Y position (0-255).
3rd: bits 7-4 is palette offset, bit 3 is X mirror, bit 2 is Y mirror, bit 1 is the rotate flag and bit 0 is X MSB.
4th: bit 7 is the visible flag, bit 6 is reserved, bits 5-0 is Name (pattern index, 0-63).
*/

                //NOTA: puede que todas las caracteristicas de los sprites no esten reflejadas correctamente aqui


				z80_int x=tbsprite_new_sprites[current_sprite][0]; //
				z80_byte y=tbsprite_new_sprites[current_sprite][1];  //

				z80_byte byte_3=tbsprite_new_sprites[current_sprite][2];
				z80_byte paloff=byte_3 & 0xF0; //
				z80_byte mirror_x=byte_3 & 8; //
				z80_byte mirror_y=byte_3 & 4; //
				z80_byte rotate=byte_3 & 2; //
				z80_byte msb_x=byte_3 &1; //

				x +=msb_x*256;

				z80_byte byte_4=tbsprite_new_sprites[current_sprite][3];
				z80_byte byte_5=tbsprite_new_sprites[current_sprite][4];
				z80_byte visible=byte_4 & 128; //
				z80_byte pattern=byte_4 & 63; //

				int sprite_es_4bpp=0;
				int offset_4bpp_N6=0;
                //int sprite_es_relative=0;

                //int sprite_es_relative_composite=0;
                //int sprite_es_relative_unified=0;

				char buf_subindex_4_bit[10];

                char buf_relative_type[4]; //CMP (composite), UNI (unified)

                strcpy(buf_relative_type,"   ");

				int zoom_x=1;
				int zoom_y=1;

                if ((byte_4 & 64)==0) {
                    //Pattern de 4 bytes. Se comporta como uno de 5 bytes con ultimo byte a 0
                    byte_5=0;
                }


                //Byte 5
                //H N6 T X X Y Y Y8
                //{H,N6}  {0,1} -> relative sprite.

                if (byte_5 & 128) sprite_es_4bpp=1;
                if (byte_5 & 64) offset_4bpp_N6=1;

                if ((byte_5 & (128+64)) == 64) {
                    //sprite relative
                    //sprite_es_relative=1;
                    strcpy(buf_relative_type,"REL");
                }
                else {
                    //sprite es anchor
                    //H N6 T X X Y Y Y8
                    //T = 0 if relative sprites are composite type else 1 for unified type
                    //El tipo de sprite relativo (unified o composite) se define en el anchor,
                    //no en los bits del sprite relativo
                    if (byte_5 & 32) {
                        //sprite_es_relative_unified=1;
                        strcpy(buf_relative_type,"UNI");
                    }
                    else {
                        //sprite_es_relative_composite=1;
                        strcpy(buf_relative_type,"CMP");
                    }

                }

                z80_byte zoom_x_value=(byte_5>>3)&3;
                if (zoom_x_value) zoom_x <<=zoom_x_value;

                z80_byte zoom_y_value=(byte_5>>1)&3;
                if (zoom_y_value) zoom_y <<=zoom_y_value;


                //TODO: Y8



				if (sprite_es_4bpp) {
					sprintf(buf_subindex_4_bit,":%d",offset_4bpp_N6);
				}
				else {
					strcpy(buf_subindex_4_bit,"  ");
				}
								//012345678901234567890123456789012
								//123:1 X: 123 Y: 123 MIRX MIRY ROT
				sprintf (dumpmemoria,"%03d%s X: %3d Y: %3d %s %s %s",current_sprite,buf_subindex_4_bit,x,y,
						(mirror_x ? "MX" : "  "),(mirror_y ? "MY" : "  "),(rotate ? "ROT" : "   ")
				);
				zxvision_print_string_defaults_fillspc(menu_debug_tsconf_tbblue_msx_spritenav_draw_sprites_window,1,linea++,dumpmemoria);


				sprintf (dumpmemoria," Pattn: %2d Palof: %3d Vis: %s"
					,pattern,paloff, (visible ? "Yes" : "No ") );
				zxvision_print_string_defaults_fillspc(menu_debug_tsconf_tbblue_msx_spritenav_draw_sprites_window,1,linea++,dumpmemoria);


				sprintf(dumpmemoria," %dbpp %s ZX: %d ZY: %d",(sprite_es_4bpp ? 4 : 8) ,
                //(sprite_es_relative ? buf_relative_type : "   "),
                buf_relative_type,
                zoom_x,zoom_y);
				zxvision_print_string_defaults_fillspc(menu_debug_tsconf_tbblue_msx_spritenav_draw_sprites_window,1,linea++,dumpmemoria);

				}

			}

			if (MACHINE_HAS_VDP_9918A) {
				z80_int sprite_attribute_table=vdp_9918a_get_sprite_attribute_table();
                z80_int orig_sprite_attribute_table=sprite_attribute_table;


				int offset_sprite=current_sprite*4;


				sprite_attribute_table +=offset_sprite;


				z80_byte (*vram_read_function_pointer)(z80_int address);


				if (MACHINE_IS_COLECO) vram_read_function_pointer=coleco_read_vram_byte;
				else if (MACHINE_IS_SG1000) vram_read_function_pointer=sg1000_read_vram_byte;
                else if (MACHINE_IS_SMS) vram_read_function_pointer=sms_read_vram_byte;
				else if (MACHINE_IS_SVI) vram_read_function_pointer=svi_read_vram_byte;
				else vram_read_function_pointer=msx_read_vram_byte;

				z80_byte vert_pos;
				z80_byte horiz_pos;
				z80_byte sprite_name;
				z80_byte attr_color_etc;

                if (vdp_9918a_si_sms_video_mode4() ) {
                    vert_pos=vram_read_function_pointer(orig_sprite_attribute_table+current_sprite);
                    horiz_pos=vram_read_function_pointer(orig_sprite_attribute_table+0x80+current_sprite*2);
                    sprite_name=vram_read_function_pointer(orig_sprite_attribute_table+0x80+current_sprite*2+1);
                }
                else {
				    vert_pos=vram_read_function_pointer(sprite_attribute_table);
				    horiz_pos=vram_read_function_pointer(sprite_attribute_table+1);
				    sprite_name=vram_read_function_pointer(sprite_attribute_table+2);
				    attr_color_etc=vram_read_function_pointer(sprite_attribute_table+3);
                }

				vert_pos++;

				if (menu_debug_spritenav_raw.v) {

                    if (vdp_9918a_si_sms_video_mode4() ) {
                        sprintf (dumpmemoria,"%03d %02X %02X %02X",current_sprite,vert_pos,horiz_pos,sprite_name);
                    }

                    else  {

				        sprintf (dumpmemoria,"%03d %02X %02X %02X %02X",current_sprite,vert_pos,horiz_pos,sprite_name,attr_color_etc);
                    }



				}

				else {


                    sprintf (dumpmemoria,"%02d X: %3d Y: %3d",
                        current_sprite,horiz_pos,vert_pos);

                    zxvision_print_string_defaults_fillspc(menu_debug_tsconf_tbblue_msx_spritenav_draw_sprites_window,1,linea++,dumpmemoria);


                    if (vdp_9918a_si_sms_video_mode4() ) {
                        sprintf (dumpmemoria," Name: %3d",sprite_name);
                    }
                    else {

                        sprintf (dumpmemoria," Name: %3d Color: %02d EC: %d",
                            sprite_name,attr_color_etc & 15,(attr_color_etc>>7) & 1);
                    }


				}

				zxvision_print_string_defaults_fillspc(menu_debug_tsconf_tbblue_msx_spritenav_draw_sprites_window,1,linea++,dumpmemoria);


			}


		}

	zxvision_draw_window_contents(menu_debug_tsconf_tbblue_msx_spritenav_draw_sprites_window);


}

void menu_debug_tsconf_tbblue_msx_spritenav_draw_sprites(void)
{


    menu_speech_set_tecla_pulsada(); //Si no, envia continuamente todo ese texto a speech


    //si ventana minimizada, no ejecutar todo el codigo de overlay
    if (menu_debug_tsconf_tbblue_msx_spritenav_draw_sprites_window->is_minimized) return;

    //printf("overlay sprites %d\n",contador_segundo);

    menu_debug_tsconf_tbblue_msx_spritenav_lista_sprites();



}

zxvision_window zxvision_window_tsconf_tbblue_spritenav;


void menu_debug_spritenav_new_window(zxvision_window *ventana)
{

    //Crear ventana si no existe
    if (!zxvision_if_window_already_exists(ventana)) {

        int xventana,yventana,ancho_ventana,alto_ventana,is_minimized,is_maximized,ancho_antes_minimize,alto_antes_minimize;

        if (!util_find_window_geometry("tsconftbbluespritenav",&xventana,&yventana,&ancho_ventana,&alto_ventana,
                &is_minimized,&is_maximized,&ancho_antes_minimize,&alto_antes_minimize)) {
            xventana=TSCONF_SPRITENAV_WINDOW_X;
            yventana=TSCONF_SPRITENAV_WINDOW_Y;
            ancho_ventana=TSCONF_SPRITENAV_WINDOW_ANCHO;
            alto_ventana=TSCONF_SPRITENAV_WINDOW_ALTO;
        }


        //zxvision_new_window(ventana,xventana,yventana,ancho_ventana,alto_ventana,
        //						TSCONF_SPRITENAV_WINDOW_ANCHO-1,menu_debug_tsconf_tbblue_msx_spritenav_get_total_height_win(),"Sprite navigator");

        zxvision_new_window_gn_cim(ventana,xventana,yventana,ancho_ventana,alto_ventana,
                                TSCONF_SPRITENAV_WINDOW_ANCHO-1,menu_debug_tsconf_tbblue_msx_spritenav_get_total_height_win(),"Sprite navigator",
                                "tsconftbbluespritenav",is_minimized,is_maximized,ancho_antes_minimize,alto_antes_minimize);

        ventana->can_be_backgrounded=1;
        ventana->upper_margin=1;
        //indicar nombre del grabado de geometria
        //strcpy(ventana->geometry_name,"tsconftbbluespritenav");
        //restaurar estado minimizado de ventana
        //ventana->is_minimized=is_minimized;

    }

    //Si ya existe, activar esta ventana
    else {

        zxvision_activate_this_window(ventana);
    }

	zxvision_draw_window(ventana);

    menu_debug_tsconf_tbblue_msx_spritenav_draw_sprites_window=ventana; //Decimos que el overlay lo hace sobre la ventana que tenemos aqui


    //cambio overlay
    zxvision_set_window_overlay(ventana,  menu_debug_tsconf_tbblue_msx_spritenav_draw_sprites);
}



void menu_debug_spritenav_save_geometry(zxvision_window *ventana)
{

	util_add_window_geometry_compact(ventana);
}

void menu_debug_tsconf_tbblue_msx_spritenav(MENU_ITEM_PARAMETERS)
{
	menu_espera_no_tecla();
	menu_reset_counters_tecla_repeticion();


	//zxvision_window ventana;
    zxvision_window *ventana;
    ventana=&zxvision_window_tsconf_tbblue_spritenav;

	//IMPORTANTE! no crear ventana si ya existe. Esto hay que hacerlo en todas las ventanas que permiten background.
	//si no se hiciera, se crearia la misma ventana, y en la lista de ventanas activas , al redibujarse,
	//la primera ventana repetida apuntaria a la segunda, que es el mismo puntero, y redibujaria la misma, y se quedaria en bucle colgado
	//zxvision_delete_window_if_exists(ventana);

	menu_debug_spritenav_new_window(ventana);



       //Toda ventana que este listada en zxvision_known_window_names_array debe permitir poder salir desde aqui
       //Se sale despues de haber inicializado overlay y de cualquier otra variable que necesite el overlay
       if (zxvision_currently_restoring_windows_on_start) {
               //printf ("Saliendo de ventana ya que la estamos restaurando en startup\n");
               return;
       }


	z80_byte tecla;

		//Si no esta multitarea, hacer un refresco inicial para que aparezca el contenido de la ventana sin tener que pulsar una tecla
		//dado que luego funciona como overlay, el overlay se aplica despues de hacer el render
		//esto solo es necesario para ventanas que usan overlay
	    if (!menu_multitarea) {
			//printf ("refresca pantalla inicial\n");
			menu_refresca_pantalla();
		}


    do {
    	menu_speech_reset_tecla_pulsada(); //Que envie a speech
   		tecla=zxvision_common_getkey_refresh();
		zxvision_handle_cursors_pgupdn(ventana,tecla);

		 if (tecla=='r') {
		 	menu_debug_spritenav_save_geometry(ventana);
		 	zxvision_destroy_window(ventana);
		 	menu_debug_spritenav_raw.v ^=1;
		 	menu_debug_spritenav_new_window(ventana);
        }
	} while (tecla!=2 && tecla!=3);




    menu_debug_spritenav_save_geometry(ventana);


	if (tecla==3) {
		zxvision_message_put_window_background();
	}

	else {

		zxvision_destroy_window(ventana);
	}

}




#define TSCONF_TILENAV_WINDOW_ANCHO 32
#define TSCONF_TILENAV_WINDOW_ALTO 24
#define TSCONF_TILENAV_WINDOW_X (menu_center_x()-TSCONF_TILENAV_WINDOW_ANCHO/2 )
#define TSCONF_TILENAV_WINDOW_Y (menu_center_y()-TSCONF_TILENAV_WINDOW_ALTO/2)
#define TSCONF_TILENAV_TILES_VERT_PER_WINDOW 64
#define TSCONF_TILENAV_TILES_HORIZ_PER_WINDOW 64





int menu_debug_tsconf_tbblue_msx_tilenav_current_tilelayer=0;

z80_bit menu_debug_tsconf_tbblue_msx_tilenav_showmap={0};

zxvision_window *menu_debug_tsconf_tbblue_msx_tilenav_lista_tiles_window;


#define DEBUG_TSCONF_TILENAV_MAX_TILES (64*64)
//#define DEBUG_TBBLUE_TILENAV_MAX_TILES_8032 (80*32)
//#define DEBUG_TBBLUE_TILENAV_MAX_TILES_4032 (40*32)


char menu_debug_tsconf_tbblue_msx_tiles_retorna_visualchar(int tnum)
{
	//Hacer un conjunto de 64 caracteres. Mismo set de caracteres que para Base64. Por que? Por que si :)
			   //0123456789012345678901234567890123456789012345678901234567890123
	char *caracter_list="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";

	int index=tnum % 64;
	return caracter_list[index];
}

int menu_debug_tsconf_tbblue_msx_tilenav_total_vert(void)
{

	int limite_vertical;

	if (MACHINE_IS_TSCONF) {
		limite_vertical=DEBUG_TSCONF_TILENAV_MAX_TILES;
		if (menu_debug_tsconf_tbblue_msx_tilenav_showmap.v) limite_vertical=TSCONF_TILENAV_TILES_VERT_PER_WINDOW;
	}

	else if (MACHINE_HAS_VDP_9918A) {
		limite_vertical=vdp_9918a_get_tile_heigth()*vdp_9918a_get_tile_width();

		if (menu_debug_tsconf_tbblue_msx_tilenav_showmap.v) limite_vertical=vdp_9918a_get_tile_heigth();
	}

	else  { //TBBLUE
		limite_vertical=tbblue_get_tilemap_width()*32;

		if (menu_debug_tsconf_tbblue_msx_tilenav_showmap.v) limite_vertical=32;
	}

	return limite_vertical;
}

//Muestra lista de tiles
void menu_debug_tsconf_tbblue_msx_tilenav_lista_tiles(void)
{

	//Suficientemente grande para almacenar regla superior en modo visual
	//80 + 3 espacios izquierda + 0 final
#define DEBUG_TILENAV_TEXTO_LINEA 84
	char dumpmemoria[DEBUG_TILENAV_TEXTO_LINEA];


	//int limite;

	int linea=0;
	//limite=DEBUG_TSCONF_TILENAV_MAX_TILES;

	int current_tile;

	z80_byte *puntero_tilemap;
	z80_byte *puntero_tilemap_orig;

	z80_int msx_pattern_name_table;

	if (MACHINE_IS_TSCONF) {
		puntero_tilemap=tsconf_ram_mem_table[0]+tsconf_return_tilemappage();
	}

	else if (MACHINE_HAS_VDP_9918A) {
		msx_pattern_name_table=vdp_9918a_get_pattern_name_table();
		puntero_tilemap=NULL; //no se usa, pero para evitar warnings del compilador
	}

	else {  //TBBLUE
		//puntero_tilemap=tbblue_ram_mem_table[5]+tbblue_get_offset_start_tilemap();
		z80_byte page_tilemap=tbblue_get_ram_page_tilemap();
		puntero_tilemap=tbblue_ram_memory_pages[page_tilemap]+(256*tbblue_get_offset_start_tilemap());
		//printf ("%XH\n",tbblue_get_offset_start_tilemap() );

	}

	z80_byte tbblue_tilemap_control;
	int tilemap_width;


	int tbblue_bytes_per_tile=2;

	if (MACHINE_IS_TBBLUE) {
					/*
					(R/W) 0x6B (107) => Tilemap Control
  bit 7    = 1 to enable the tilemap
  bit 6    = 0 for 40x32, 1 for 80x32
  bit 5    = 1 to eliminate the attribute entry in the tilemap
  bit 4    = palette select
  bits 3-0 = Reserved set to 0
					*/
					tbblue_tilemap_control=tbblue_registers[107];

					if (tbblue_tilemap_control&32) tbblue_bytes_per_tile=1;

					tilemap_width=tbblue_get_tilemap_width();

	}

	if (MACHINE_HAS_VDP_9918A) {
		tilemap_width=vdp_9918a_get_tile_width();
	}

	puntero_tilemap_orig=puntero_tilemap;

	int limite_vertical=menu_debug_tsconf_tbblue_msx_tilenav_total_vert();


	int offset_vertical=0;

	if (menu_debug_tsconf_tbblue_msx_tilenav_showmap.v) {
		if (MACHINE_IS_TSCONF) {
				  //0123456789012345678901234567890123456789012345678901234567890123
		strcpy(dumpmemoria,"   0    5    10   15   20   25   30   35   40   45   50   55   60  ");
		}

		else if (MACHINE_HAS_VDP_9918A) {
			if (tilemap_width==32) {
				  			 //01234567890123456789012345678901
		strcpy(dumpmemoria,"   0    5    10   15   20   25   30        ");
			}
			else {
				  			 //0123456789012345678901234567890123456789012345678901234567890123
		strcpy(dumpmemoria,"   0    5    10   15   20   25   30   35   ");
			}
		}

		else { //TBBLUE
			if (tilemap_width==40) {
				             //0123456789012345678901234567890123456789012345678901234567890123
		strcpy(dumpmemoria,"   0    5    10   15   20   25   30   35                                           ");
			}
			else {
				             //01234567890123456789012345678901234567890123456789012345678901234567890123456789
		strcpy(dumpmemoria,"   0    5    10   15   20   25   30   35   40   45   50   55   60   65   70   75   ");
			}

		}

		//Indicar codigo 0 de final
		//dumpmemoria[current_tile_x+TSCONF_TILENAV_TILES_HORIZ_PER_WINDOW+3]=0;  //3 espacios al inicio

		//menu_escribe_linea_opcion(linea++,-1,1,&dumpmemoria[current_tile_x]); //Mostrar regla superior
		zxvision_print_string_defaults(menu_debug_tsconf_tbblue_msx_tilenav_lista_tiles_window,1,0,dumpmemoria);
	}
	else {
		//Aumentarlo en cuanto al offset que estamos (si modo lista)

		int offset_y=menu_debug_tsconf_tbblue_msx_tilenav_lista_tiles_window->offset_y;


		offset_vertical=offset_y/2;
		linea=offset_vertical*2;

        int ventana_alto_visible=menu_debug_tsconf_tbblue_msx_tilenav_lista_tiles_window->visible_height;

        int lineas_en_ventana=((ventana_alto_visible-2)/2)+1; //El maximo que cabe en pantalla, +1 para cuando se baja 1 posicion con cursor

		limite_vertical=offset_vertical+lineas_en_ventana;

	}

	//linea destino es +3, pues las tres primeras son de leyenda
	linea +=3;



		for (;offset_vertical<limite_vertical;offset_vertical++) {

				//texto linea inicializarlo siempre con espacios
				//#define DEBUG_TILENAV_TEXTO_LINEA 84
				//char dumpmemoria[DEBUG_TILENAV_TEXTO_LINEA];
				int i;
				for (i=0;i<DEBUG_TILENAV_TEXTO_LINEA-1;i++) {
					dumpmemoria[i]=' ';
				}

				//Y 0 del final
				dumpmemoria[i]=0;

			int repetir_ancho=1;
			int mapa_tile_x=3;
			if (menu_debug_tsconf_tbblue_msx_tilenav_showmap.v==0) {
				//Modo lista tiles
				current_tile=offset_vertical;
			}

			else {
				//Modo mapa tiles
				if (MACHINE_IS_TSCONF) {
					current_tile=offset_vertical*64;
					repetir_ancho=TSCONF_TILENAV_TILES_HORIZ_PER_WINDOW;

					//poner regla vertical
					int linea_tile=current_tile/64;
					if ( (linea_tile%5)==0) sprintf (dumpmemoria,"%2d ",linea_tile);
					else sprintf (dumpmemoria,"   ");
				}

				else if (MACHINE_HAS_VDP_9918A) {
					current_tile=offset_vertical*tilemap_width;
					repetir_ancho=tilemap_width;

					//poner regla vertical
					int linea_tile=current_tile/tilemap_width;
					if ( (linea_tile%5)==0) sprintf (dumpmemoria,"%2d ",linea_tile);
					else sprintf (dumpmemoria,"   ");
				}

				else { //TBBLUE
					current_tile=offset_vertical*tilemap_width;
					repetir_ancho=tilemap_width;

					//poner regla vertical
					int linea_tile=current_tile/tilemap_width;
					if ( (linea_tile%5)==0) sprintf (dumpmemoria,"%2d ",linea_tile);
					else sprintf (dumpmemoria,"   ");
				}
			}

			//printf ("linea: %3d current tile: %10d puntero: %10d\n",linea_color,current_tile,puntero_tilemap-tsconf_ram_mem_table[0]-tsconf_return_tilemappage()	);

			do {



				if (MACHINE_IS_TSCONF) {
					int y=current_tile/64;
					int x=current_tile%64;

					//printf ("x: %d y: %d\n",x,y);


					int offset=(256*y)+(x*2);

					offset+=menu_debug_tsconf_tbblue_msx_tilenav_current_tilelayer*128;

					int tnum=puntero_tilemap[offset]+256*(puntero_tilemap[offset+1]&0xF);

					//printf ("Current tile: %d  x: %d y: %d  tnum: %d\n",current_tile,x,y,tnum);

					z80_byte tnum_x=tnum&63;
					z80_byte tnum_y=(tnum>>6)&63;

		    		z80_byte tpal=(puntero_tilemap[offset+1]>>4)&3;

					z80_byte tile_xf=puntero_tilemap[offset+1]&64;
					z80_byte tile_yf=puntero_tilemap[offset+1]&128;

					if (menu_debug_tsconf_tbblue_msx_tilenav_showmap.v==0) {
						//Modo lista tiles
						sprintf (dumpmemoria,"X: %3d Y: %3d                   ",x,y);

						zxvision_print_string_defaults(menu_debug_tsconf_tbblue_msx_tilenav_lista_tiles_window,1,linea++,dumpmemoria);

						sprintf (dumpmemoria," Tile: %2d,%2d %s %s P:%2d",tnum_x,tnum_y,
							(tile_xf ? "XF" : "  "),(tile_yf ? "YF": "  "),
							tpal );

						zxvision_print_string_defaults(menu_debug_tsconf_tbblue_msx_tilenav_lista_tiles_window,1,linea++,dumpmemoria);
					}
					else {
						//Modo mapa tiles
						z80_byte caracter_final;

						if (tnum==0) {
							caracter_final=' ';
						}
						else {
							caracter_final=menu_debug_tsconf_tbblue_msx_tiles_retorna_visualchar(tnum);
						}

						dumpmemoria[mapa_tile_x++]=caracter_final;
					}
				}

				if (MACHINE_HAS_VDP_9918A) {
					int y=current_tile/tilemap_width;
					int x=current_tile%tilemap_width;

					int tnum;

                    int sms_mirror_x;

                    int sms_mirror_y;

                    int sms_palette_offset;

                    int sms_priority_tile_bit;


					if (MACHINE_IS_COLECO) {
						tnum=coleco_read_vram_byte(msx_pattern_name_table+current_tile);
					}

					else if (MACHINE_IS_SG1000) {
						tnum=sg1000_read_vram_byte(msx_pattern_name_table+current_tile);
					}

					else if (MACHINE_IS_SMS) {
                        if (vdp_9918a_si_sms_video_mode4()) {
                            int offset_tile=current_tile*2;

                            z80_int pattern_word=sms_read_vram_byte(msx_pattern_name_table+offset_tile)+256*sms_read_vram_byte(msx_pattern_name_table+offset_tile+1);

                            tnum=pattern_word & 511;

                            sms_mirror_x=(pattern_word & 0x0200);

                            sms_mirror_y=(pattern_word & 0x0400);

                            sms_palette_offset=(pattern_word & 0x0800 ? 16 : 0);

                            sms_priority_tile_bit=(pattern_word & 0x1000);
                        }
						else tnum=sms_read_vram_byte(msx_pattern_name_table+current_tile);
					}

					else if (MACHINE_IS_SVI) {
						tnum=svi_read_vram_byte(msx_pattern_name_table+current_tile);
					}

					else {
						tnum=msx_read_vram_byte(msx_pattern_name_table+current_tile);
					}


					if (menu_debug_tsconf_tbblue_msx_tilenav_showmap.v==0) {
						//Modo lista tiles
						sprintf (dumpmemoria,"X: %3d Y: %3d                   ",x,y);

						zxvision_print_string_defaults(menu_debug_tsconf_tbblue_msx_tilenav_lista_tiles_window,1,linea++,dumpmemoria);

                        if (vdp_9918a_si_sms_video_mode4()) {
                            sprintf (dumpmemoria," Tile: %3d %s %s %s Off: %2d",tnum,
                                    (sms_mirror_x ? "MX" : "  "),
                                    (sms_mirror_y ? "MY" : "  "),
                                    (sms_priority_tile_bit ? "PRI" : "   "),
                                    sms_palette_offset
                            );
                        }
                        else {
						    sprintf (dumpmemoria," Tile: %3d %c",tnum,(tnum>=33 && tnum<=126 ? tnum : ' ' ));
                        }

						zxvision_print_string_defaults(menu_debug_tsconf_tbblue_msx_tilenav_lista_tiles_window,1,linea++,dumpmemoria);

					}
					else {
						//Modo mapa tiles
						int caracter_final;

						if (tnum==0) {
							caracter_final=' ';
						}
						else {

							//Si caracter imprimible, mostramos. Si no, mostrar set alternativo
							if (tnum>=32 && tnum<=126) {
								caracter_final=tnum;
							}
							else {
								caracter_final=menu_debug_tsconf_tbblue_msx_tiles_retorna_visualchar(tnum);
							}
						}

						dumpmemoria[mapa_tile_x++]=caracter_final;
					}

				}

				if (MACHINE_IS_TBBLUE) {

					int y=current_tile/tilemap_width;
					int x=current_tile%tilemap_width;

					int offset=(tilemap_width*tbblue_bytes_per_tile*y)+(x*tbblue_bytes_per_tile);
					/*
					 bits 15-12 : palette offset
  bit     11 : x mirror
  bit     10 : y mirror
  bit      9 : rotate
  bit      8 : ULA over tilemap (if the ula is disabled, bit 8 of tile number)
  bits   7-0 : tile number
					*/

					int xmirror,ymirror,rotate;
					z80_byte tpal;

					z80_byte byte_first;
					z80_byte byte_second;

					byte_first=puntero_tilemap[offset];
					byte_second=puntero_tilemap[offset+1];

					int tnum=byte_first;
					int ula_over_tilemap;

					z80_byte tbblue_default_tilemap_attr=tbblue_registers[108];

					if (tbblue_bytes_per_tile==1) {
						/*
						(R/W) 0x6C (108) => Default Tilemap Attribute
  bits 7-4 = Palette Offset
  bit 3    = X mirror
  bit 2    = Y mirror
  bit 1    = Rotate
  bit 0    = ULA over tilemap
             (bit 8 of tile id if the ULA is disabled)
			 			*/
					 	tpal=(tbblue_default_tilemap_attr>>4)&15;
						xmirror=(tbblue_default_tilemap_attr>>3)&1;
						ymirror=(tbblue_default_tilemap_attr>>2)&1;
						rotate=(tbblue_default_tilemap_attr>>1)&1;

						if (tbblue_if_ula_is_enabled() ) {
						/*
						108
						  bit 0    = ULA over tilemap
             (bit 8 of tile id if the ULA is disabled)
						*/
							ula_over_tilemap=tbblue_default_tilemap_attr &1;
						}

						else {
							tnum |=(tbblue_default_tilemap_attr&1)<<8; // bit      8 : ULA over tilemap (if the ula is disabled, bit 8 of tile number)
						}

					}

					else {
						/*

					 bits 15-12 : palette offset
  bit     11 : x mirror
  bit     10 : y mirror
  bit      9 : rotate
  bit      8 : ULA over tilemap (if the ula is disabled, bit 8 of tile number)
					*/
					 	tpal=(byte_second>>4)&15;
						xmirror=(byte_second>>3)&1;
						ymirror=(byte_second>>2)&1;
						rotate=(byte_second>>1)&1;
						//ula_over_tilemap=byte_second &1;

					if (tbblue_if_ula_is_enabled() ) {
						/*
						  bit      8 : ULA over tilemap (if the ula is disabled, bit 8 of tile number)
						*/
							ula_over_tilemap=byte_second &1;
						}

						else {
							tnum |=(byte_second&1)<<8; // bit      8 : ULA over tilemap (if the ula is disabled, bit 8 of tile number)
						}


					}

					//printf ("tnum: %d\n",tnum);


					if (menu_debug_tsconf_tbblue_msx_tilenav_showmap.v==0) {
						//Modo lista tiles
						sprintf (dumpmemoria,"X: %3d Y: %3d                   ",x,y);

						zxvision_print_string_defaults(menu_debug_tsconf_tbblue_msx_tilenav_lista_tiles_window,1,linea++,dumpmemoria);

						sprintf (dumpmemoria," Tile: %3d %s %s %s %s P:%2d ",tnum,
							(xmirror ? "MX" : "  "),(ymirror ? "MY": "  "),
							(rotate ? "R" : " "),(ula_over_tilemap ? "U": " "),
							tpal );

						zxvision_print_string_defaults(menu_debug_tsconf_tbblue_msx_tilenav_lista_tiles_window,1,linea++,dumpmemoria);
					}
					else {
						//Modo mapa tiles
						int caracter_final;

						if (tnum==0) {
							caracter_final=' ';
						}
						else {
							caracter_final=menu_debug_tsconf_tbblue_msx_tiles_retorna_visualchar(tnum);
						}

						dumpmemoria[mapa_tile_x++]=caracter_final;
					}


				}

				current_tile++;

				repetir_ancho--;
			} while (repetir_ancho);

			if (menu_debug_tsconf_tbblue_msx_tilenav_showmap.v) {
				zxvision_print_string_defaults(menu_debug_tsconf_tbblue_msx_tilenav_lista_tiles_window,1,linea++,dumpmemoria);
				puntero_tilemap=puntero_tilemap_orig;
			}

		}



	//return linea;

	zxvision_draw_window_contents(menu_debug_tsconf_tbblue_msx_tilenav_lista_tiles_window);
}

void menu_debug_tsconf_tbblue_msx_tilenav_draw_tiles(void)
{

    menu_speech_set_tecla_pulsada(); //Si no, envia continuamente todo ese texto a speech



    //si ventana minimizada, no ejecutar todo el codigo de overlay
    if (menu_debug_tsconf_tbblue_msx_tilenav_lista_tiles_window->is_minimized) return;

    //printf("overlay tiles %d\n",contador_segundo);

    menu_debug_tsconf_tbblue_msx_tilenav_lista_tiles();

}



void menu_debug_tsconf_tbblue_msx_tilenav_new_window(zxvision_window *ventana)
{

    char titulo[33];

    char linea_leyenda[64];
    sprintf (titulo,"Tile Navigator");

    //Forzar a mostrar atajos
    z80_bit antes_menu_writing_inverse_color;
    antes_menu_writing_inverse_color.v=menu_writing_inverse_color.v;
    menu_writing_inverse_color.v=1;

    int total_height=menu_debug_tsconf_tbblue_msx_tilenav_total_vert();
    int total_width=31;

    char texto_layer[32];

    //En caso de tbblue y msx, solo hay una capa
    if (MACHINE_IS_TBBLUE || MACHINE_HAS_VDP_9918A) texto_layer[0]=0;

    else sprintf (texto_layer,"~~Layer %d",menu_debug_tsconf_tbblue_msx_tilenav_current_tilelayer);

    if (menu_debug_tsconf_tbblue_msx_tilenav_showmap.v) {
        sprintf (linea_leyenda,"~~Mode: Visual %s",texto_layer);

        if (MACHINE_IS_TSCONF) {
        total_width=TSCONF_TILENAV_TILES_HORIZ_PER_WINDOW+4;
        }
        else if (MACHINE_HAS_VDP_9918A) {
            //Le ponemos siempre el maximo
            total_width=40+4;
        }
        else {
            //TBBLUE
            total_width=tbblue_get_tilemap_width()+4;
        }

    }

    else {
        sprintf (linea_leyenda,"~~Mode: List %s",texto_layer);
        total_height*=2;
    }

    //tres mas para ubicar las lineas de leyenda
    total_height+=3;

    //total_height+=2;

    //Crear ventana si no existe
    if (!zxvision_if_window_already_exists(ventana)) {

        int xventana,yventana,ancho_ventana,alto_ventana,is_minimized,is_maximized,ancho_antes_minimize,alto_antes_minimize;

        if (!util_find_window_geometry("tsconftbbluetilenav",&xventana,&yventana,&ancho_ventana,&alto_ventana,
                &is_minimized,&is_maximized,&ancho_antes_minimize,&alto_antes_minimize)) {
            xventana=TSCONF_TILENAV_WINDOW_X;
            yventana=TSCONF_TILENAV_WINDOW_Y;
            ancho_ventana=TSCONF_TILENAV_WINDOW_ANCHO;
            alto_ventana=TSCONF_TILENAV_WINDOW_ALTO;
        }

        //zxvision_new_window(ventana,xventana,yventana,ancho_ventana,alto_ventana,total_width,total_height,titulo);

        zxvision_new_window_gn_cim(ventana,xventana,yventana,
                            ancho_ventana,alto_ventana,
                            total_width,total_height,titulo,"tsconftbbluetilenav",is_minimized,is_maximized,ancho_antes_minimize,alto_antes_minimize);


        //Establecer leyenda en la parte de abajo
        ventana->lower_margin=2;
        //Texto sera el de la primera linea
        ventana->upper_margin=1;

        ventana->can_be_backgrounded=1;
        //indicar nombre del grabado de geometria
        //strcpy(ventana->geometry_name,"tsconftbbluetilenav");
        //restaurar estado minimizado de ventana
        //ventana->is_minimized=is_minimized;

        //Permitir hotkeys desde raton
        ventana->can_mouse_send_hotkeys=1;

    }

    //Si ya existe, activar esta ventana
    else {
        //Quitando el overlay de dicha ventana para que no se redibuje dos veces (con su overlay y luego con draw below windows)
        //TODO: esto en un futuro probablemente se hara el redibujado desde draw below cuando esta activa, por tanto este NULL no se pondra
        ventana->overlay_function=NULL;

        zxvision_activate_this_window(ventana);
    }

    //Leyenda inferior
    //zxvision_print_string_defaults_fillspc(ventana,1,1,"-----");
    zxvision_print_string_defaults_fillspc(ventana,1,2,linea_leyenda);

    zxvision_draw_window(ventana);

    //Restaurar comportamiento atajos
    menu_writing_inverse_color.v=antes_menu_writing_inverse_color.v;
    //Nota: los atajos se "pintan" en la memoria de la ventana ya con el color inverso
    //por tanto con meter al principio la variable de inverse_color es suficiente
    //y no hay que activar inverse color cada vez que se redibuja ventana,
    //pues al redibujar ventana está leyendo el contenido de la memoria de la ventana, y ahí ya está con color inverso

    menu_debug_tsconf_tbblue_msx_tilenav_lista_tiles_window=ventana; //Decimos que el overlay lo hace sobre la ventana que tenemos aqui

    //cambio overlay
    zxvision_set_window_overlay(ventana,menu_debug_tsconf_tbblue_msx_tilenav_draw_tiles);


}


void menu_debug_tsconf_tbblue_msx_save_geometry(zxvision_window *ventana)
{
	util_add_window_geometry_compact(ventana);
}

zxvision_window zxvision_window_tsconf_tbblue_tilenav;

void menu_debug_tsconf_tbblue_msx_tilenav(MENU_ITEM_PARAMETERS)
{

	menu_espera_no_tecla();
	menu_reset_counters_tecla_repeticion();


	zxvision_window *ventana;
	ventana=&zxvision_window_tsconf_tbblue_tilenav;

    //IMPORTANTE! no crear ventana si ya existe. Esto hay que hacerlo en todas las ventanas que permiten background.
    //si no se hiciera, se crearia la misma ventana, y en la lista de ventanas activas , al redibujarse,
    //la primera ventana repetida apuntaria a la segunda, que es el mismo puntero, y redibujaria la misma, y se quedaria en bucle colgado
    //zxvision_delete_window_if_exists(ventana);

	menu_debug_tsconf_tbblue_msx_tilenav_new_window(ventana);




       //Toda ventana que este listada en zxvision_known_window_names_array debe permitir poder salir desde aqui
       //Se sale despues de haber inicializado overlay y de cualquier otra variable que necesite el overlay
       if (zxvision_currently_restoring_windows_on_start) {
               //printf ("Saliendo de ventana ya que la estamos restaurando en startup\n");
               return;
       }


	z80_byte tecla;

	//Si no esta multitarea, hacer un refresco inicial para que aparezca el contenido de la ventana sin tener que pulsar una tecla
	//dado que luego funciona como overlay, el overlay se aplica despues de hacer el render
	//esto solo es necesario para ventanas que usan overlay
	if (!menu_multitarea) {
		//printf ("refresca pantalla inicial\n");
		menu_refresca_pantalla();
	}


	do {
    	menu_speech_reset_tecla_pulsada(); //Que envie a speech

		tecla=zxvision_common_getkey_refresh();


		switch (tecla) {

			case 'l':
				//En caso de tbblue, hay una sola capa
				if (!MACHINE_IS_TBBLUE) {
					menu_debug_tsconf_tbblue_msx_save_geometry(ventana);
					zxvision_destroy_window(ventana);
					menu_debug_tsconf_tbblue_msx_tilenav_current_tilelayer ^=1;
					menu_debug_tsconf_tbblue_msx_tilenav_new_window(ventana);
				}
			break;

			case 'm':
				menu_debug_tsconf_tbblue_msx_save_geometry(ventana);
				zxvision_destroy_window(ventana);
				menu_debug_tsconf_tbblue_msx_tilenav_showmap.v ^=1;
				menu_debug_tsconf_tbblue_msx_tilenav_new_window(ventana);

			break;


			default:
				zxvision_handle_cursors_pgupdn(ventana,tecla);
			break;
		}



	} while (tecla!=2 && tecla!=3);




    //Grabar geometria ventana. Usamos funcion auxiliar pues la llamamos tambien al cambiar de modo y layer
	menu_debug_tsconf_tbblue_msx_save_geometry(ventana);


    if (tecla==3) {
		zxvision_message_put_window_background();
	}

	else {

		zxvision_destroy_window(ventana);
	}



}




//#define SOUND_WAVE_X (menu_origin_x()+1)
//#define SOUND_WAVE_Y 3
#define SOUND_WAVE_ANCHO 30
#define SOUND_WAVE_ALTO 15


//Tipo soundwave: 0=vacia, 1=llena, 2=scroll
int menu_sound_wave_llena=0;
int menu_audio_draw_sound_wave_ycentro;

char menu_audio_draw_sound_wave_valor_medio,menu_audio_draw_sound_wave_valor_max,menu_audio_draw_sound_wave_valor_min;
int menu_audio_draw_sound_wave_frecuencia_aproximada;

int menu_audio_draw_sound_wave_volumen=0;
int menu_audio_draw_sound_wave_volumen_escalado=0;


//Usado dentro del overlay de waveform, para mostrar dos veces por segundo el texto que average, etc
int menu_waveform_valor_contador_segundo_anterior;

int menu_waveform_previous_volume=0;

#define MAX_ALTO_WAVEFORM_PIXEL_ARRAY 512
#define MAX_ANCHO_WAVEFORM_PIXEL_ARRAY 1024

//usado para el tipo 2 que hace scroll
int *menu_waveform_pixel_array=NULL;

zxvision_window *menu_audio_draw_sound_wave_window;

z80_bit menu_waveform_separar_canales={0};

void menu_waveform_empty_array(void)
{
    //Establecer array a 0
    int total;
    total=MAX_ANCHO_WAVEFORM_PIXEL_ARRAY*MAX_ALTO_WAVEFORM_PIXEL_ARRAY;

    int i;
    for (i=0;i<total;i++) menu_waveform_pixel_array[i]=ESTILO_GUI_PAPEL_NORMAL;
}

void menu_waveform_putpixel_array(int x,int y,int color)
{
    if (x<0 || y<0 || x>=MAX_ANCHO_WAVEFORM_PIXEL_ARRAY || y>=MAX_ALTO_WAVEFORM_PIXEL_ARRAY) {
        //printf("Fuera de rango %d,%d\n",x,y);
        return;
    }

    int offset_destino=y*MAX_ANCHO_WAVEFORM_PIXEL_ARRAY+x;
    menu_waveform_pixel_array[offset_destino]=color;
}

//para compatibilidad con zxvision_draw_line
void menu_waveform_putpixel_array_from_linea(zxvision_window *ventana GCC_UNUSED,int x,int y,int color)
{
    menu_waveform_putpixel_array(x,y,color);
}

void menu_waveform_draw_array(int ancho,int alto,int xorigen,int yorigen,int forzar_color)
{
    int x,y;

    //for (y=yorigen;y<yorigen+alto;y++) {
    //    for (x=xorigen;x<xorigen+ancho;x++) {

    for (y=0;y<alto;y++) {
        for (x=0;x<ancho;x++) {

            //Y siempre que estemos en rango
            if (x<MAX_ANCHO_WAVEFORM_PIXEL_ARRAY && y<MAX_ALTO_WAVEFORM_PIXEL_ARRAY) {

                int offset_destino=y*MAX_ANCHO_WAVEFORM_PIXEL_ARRAY+x;
                int valor=menu_waveform_pixel_array[offset_destino];

                //Dibujar pixel solo si no es blanco
                //Dado que esto es overlay de pixeles, continuamente se resetea a blanco al refrescar pantalla,
                //no hace falta dibujar esos pixeles que ya son blancos
                if (valor!=ESTILO_GUI_PAPEL_NORMAL) {
                    if (forzar_color!=-1) valor=forzar_color;

                    zxvision_putpixel(menu_audio_draw_sound_wave_window,x+xorigen,y+yorigen,valor);
                }
            }
            /*else {
                //Si no, rellenar con color distinto. Rojo para avisar
                zxvision_putpixel(menu_audio_draw_sound_wave_window,x,y,ESTILO_GUI_TINTA_NO_DISPONIBLE);
            }*/

        }
    }
}

int menu_audio_draw_sound_wave_si_scroll_hilow_audio;

void menu_audio_draw_sound_wave(void)
{




	char buffer_texto_medio[40]; //32+3+margen de posible color rojo del maximo

	menu_speech_set_tecla_pulsada(); //Si no, envia continuamente todo ese texto a speech


    //si ventana minimizada, no ejecutar todo el codigo de overlay
    if (menu_audio_draw_sound_wave_window->is_minimized) return;

	//Inicializar array waveform
	if (menu_waveform_pixel_array==NULL) {
		menu_waveform_pixel_array=malloc(sizeof(int)*MAX_ANCHO_WAVEFORM_PIXEL_ARRAY*MAX_ALTO_WAVEFORM_PIXEL_ARRAY);
		if (menu_waveform_pixel_array==NULL) cpu_panic("Cannot allocate memory for waveform");

		//Establecer array a 0
		menu_waveform_empty_array();
	}




	//esto hara ejecutar esto 2 veces por segundo
	if ( ((contador_segundo%500) == 0 && menu_waveform_valor_contador_segundo_anterior!=contador_segundo) || menu_multitarea==0) {

		menu_waveform_valor_contador_segundo_anterior=contador_segundo;
		//printf ("Refrescando. contador_segundo=%d\n",contador_segundo);

            //printf("Item seleccionado: %d\n",audio_new_waveform_opcion_seleccionada);

		//menu_speech_set_tecla_pulsada(); //Si no, envia continuamente todo ese texto a speech

			//Average, min, max

			sprintf (buffer_texto_medio,"Av.: %d Min: %d Max: %d",
			    menu_audio_draw_sound_wave_valor_medio,menu_audio_draw_sound_wave_valor_min,menu_audio_draw_sound_wave_valor_max);


			//menu_escribe_linea_opcion(1,-1,1,buffer_texto_medio);
			zxvision_print_string_defaults_fillspc(menu_audio_draw_sound_wave_window,1,1,buffer_texto_medio);



			//Hacer decaer el volumen
			menu_waveform_previous_volume=menu_decae_dec_valor_volumen(menu_waveform_previous_volume,menu_audio_draw_sound_wave_volumen_escalado);


			//Frequency
			sprintf (buffer_texto_medio,"Average freq: %d Hz (%s)",
			    menu_audio_draw_sound_wave_frecuencia_aproximada,get_note_name(menu_audio_draw_sound_wave_frecuencia_aproximada));

			zxvision_print_string_defaults_fillspc(menu_audio_draw_sound_wave_window,1,3,buffer_texto_medio);


	}


	int ancho;


	//Ancho de zona waveform variable segun el tamanyo de ventana
	ancho=menu_audio_draw_sound_wave_window->visible_width-2;

	//Por si acaso, no vayamos a provocar alguna division por cero
	if (ancho<1) ancho=1;

	int alto;

	int lineas_cabecera=4;

	alto=menu_audio_draw_sound_wave_window->visible_height-lineas_cabecera-2;

	//Por si acaso, no vayamos a provocar alguna division por cero
	if (alto<1) alto=1;



	int xorigen=1;
	int yorigen;

	yorigen=lineas_cabecera;


	if (si_complete_video_driver() ) {
        	ancho *=menu_char_width;
	        alto *=8;
        	xorigen *=menu_char_width;
	        yorigen *=8;
	}



	menu_audio_draw_sound_wave_ycentro=alto/2;

	int x,y;


    //En teoria no se usa sin inicializar pero por si acaso
    int lasty=0;


	//Para drivers de texto, borrar zona

	if (!si_complete_video_driver() ) {
	        for (x=xorigen;x<xorigen+ancho;x++) {
        	        for (y=yorigen;y<yorigen+alto;y++) {
						zxvision_print_char_simple(menu_audio_draw_sound_wave_window,x,y,ESTILO_GUI_TINTA_NORMAL,ESTILO_GUI_PAPEL_NORMAL,0,' ');
	                }
        	}
	}


    //coordenada mas abajo hasta la fecha para escribir texto destacar
    int destacar_max_y=0;

	audiobuffer_stats audiostats;
	audio_get_audiobuffer_stats(&audiostats);


	menu_audio_draw_sound_wave_valor_max=audiostats.maximo;
	menu_audio_draw_sound_wave_valor_min=audiostats.minimo;
	menu_audio_draw_sound_wave_frecuencia_aproximada=audiostats.frecuencia;
	menu_audio_draw_sound_wave_volumen=audiostats.volumen;
	menu_audio_draw_sound_wave_volumen_escalado=audiostats.volumen_escalado;

    //Si convirtiendo hilow, alterar max y min especial para la vista de scroll
    //menu_hilow_convert_audio_last_audio_sample
    if (hilow_convert_audio_thread_running && menu_hilow_convert_lento) {
        menu_audio_draw_sound_wave_valor_max=menu_hilow_convert_audio_last_audio_sample;
        menu_audio_draw_sound_wave_valor_min=menu_hilow_convert_audio_last_audio_sample;
    }


	int audiomedio=audiostats.medio;
	menu_audio_draw_sound_wave_valor_medio=audiomedio;
	audiomedio=audiomedio*alto/256;

	//Lo situamos en el centro. Negativo hacia abajo (Y positiva)
	audiomedio=menu_audio_draw_sound_wave_ycentro-audiomedio;


    //Borrar de pantalla lo que habia antes
    menu_waveform_draw_array(ancho,alto,xorigen,yorigen,ESTILO_GUI_PAPEL_NORMAL);


	//Scroll izquierda de array waveform
	if (menu_sound_wave_llena==2) {
		int scroll_x,scroll_y;

		int max_x=MAX_ANCHO_WAVEFORM_PIXEL_ARRAY;
		int max_y=MAX_ALTO_WAVEFORM_PIXEL_ARRAY;

		//Solo hacemos scroll de lo que se vera
		if (ancho<max_x) max_x=ancho;
		if (alto<max_y) max_y=alto;

		//printf ("max scroll %d %d\n",max_x,max_y);

        //Si estamos convirtiendo hilow y pausados, no hacer scroll
        int hacer_scroll=1;

        if (hilow_convert_audio_thread_running) {
            if (menu_hilow_convert_paused) hacer_scroll=0;

            if (menu_hilow_convert_lento>0) {
                menu_audio_draw_sound_wave_si_scroll_hilow_audio++;
                if ( (menu_audio_draw_sound_wave_si_scroll_hilow_audio % menu_hilow_convert_lento) !=0) hacer_scroll=0;
            }
        }


        if (hacer_scroll) {

            for (scroll_y=0;scroll_y<max_y;scroll_y++) {
                for (scroll_x=0;scroll_x<max_x-1;scroll_x++) {

                    int offset_dest=scroll_y*MAX_ANCHO_WAVEFORM_PIXEL_ARRAY+scroll_x;
                    int offset_orig=offset_dest+1;
                    menu_waveform_pixel_array[offset_dest]=menu_waveform_pixel_array[offset_orig];
                }
                //Llenar el ultimo en blanco
                int offset_dest=scroll_y*MAX_ANCHO_WAVEFORM_PIXEL_ARRAY+scroll_x;
                menu_waveform_pixel_array[offset_dest]=ESTILO_GUI_PAPEL_NORMAL;
            }

        }

        //Indicar con linea vertical, el maximo y minimo
        int ydestino_min=alto/2-(menu_audio_draw_sound_wave_valor_min*alto)/256;
        int ydestino_max=alto/2-(menu_audio_draw_sound_wave_valor_max*alto)/256;


        int ydestino;

        //El valor minimo esta "mas abajo" en la pantalla, por eso en pixeles, es valor mas alto
        //El valor maximo esta mas arriba, es valor mas bajo (Y=0 esta arriba del todo)
        for (ydestino=ydestino_max;ydestino<=ydestino_min;ydestino++) {

            menu_waveform_putpixel_array(ancho-1,ydestino,ESTILO_GUI_COLOR_WAVEFORM);

        }
	}


	int puntero_audio=0;
	char valor_audio;


	if (menu_sound_wave_llena<2) {
        //borrar lo que haya en el array
        menu_waveform_empty_array();

        int canal=0;
        int total_canales=1;

        if (menu_waveform_separar_canales.v) total_canales=2;

        for (canal=0;canal<total_canales;canal++) {

            puntero_audio=0;


            int xinicial_grafica;
            int ancho_grafica;

            if (menu_waveform_separar_canales.v) {
                ancho_grafica=ancho/2;
                xinicial_grafica=ancho_grafica*canal;
            }

            else  {
                xinicial_grafica=0;
                ancho_grafica=ancho;
            }



        //Total ancho para cada canal
        for (x=xinicial_grafica;x<xinicial_grafica+ancho_grafica;x++) {


			//Obtenemos valor medio de audio
			int valor_medio=0;

			//Calcular cuantos valores representa un pixel, teniendo en cuenta maximo buffer
			const int max_valores=AUDIO_BUFFER_SIZE/ancho_grafica;

			int valores=max_valores;
			for (;valores>0;valores--,puntero_audio++) {
				if (puntero_audio>=AUDIO_BUFFER_SIZE) {
					//por si el calculo no es correcto, salir.
					//esto no deberia suceder ya que el calculo de max_valores se hace en base al maximo
					cpu_panic("menu_audio_draw_sound_wave: pointer beyond AUDIO_BUFFER_SIZE");
				}


                int suma_canales;

                //Canales separados
                if (menu_waveform_separar_canales.v) {
                    suma_canales=audio_buffer[canal+puntero_audio*2];
                    valor_medio=valor_medio+suma_canales;
                }

                else {

                //Stereo junto

                    suma_canales=audio_buffer[puntero_audio*2]+audio_buffer[(puntero_audio*2)+1];
                    suma_canales /=2;
                    valor_medio=valor_medio+suma_canales;
                }



			}

			valor_medio=valor_medio/max_valores;

            //Para visualizar la forma de onda en el conversor de sonido a zx81
            /*
                Con ventana de conversion de audio a zx81, hacemos aquí en waveform
                - En zoom 1, unir cada valor de sample anterior con una linea. Lo dibujamos separados 4 pixeles
                - Al aumentar zoom, esa separacion se va reduciendo: zoom 2, cada 2 pixeles. zoom 4 o superior, cada pixel
                - A zoom > 1, valor de sample mostrado es la media de 2 valores (zoom 2) o 4 valores (zoom 4) etc
                - Cuando el zoom es >=16, mostramos tambien en color algo mas oscuro linea vertical indicando valor mínimo y máximo
                Todo eso se hace aqui a continuación y un poco mas abajo, buscar por condición menu_convert_audio_to_zx81_window_running
            */
            //Queremos que cada valor de sample esté separado 4 pixeles así podemos unirlo con lineas y se ve mejor
            int color_linea=ESTILO_GUI_COLOR_WAVEFORM;
            int cada_cuanto_convert_zx81=4;

            int convert_zx81_max_valor=0;
            int convert_zx81_min_valor=255;

            if (menu_convert_audio_to_zx81_zoom_wave==2) cada_cuanto_convert_zx81=2;
            else if (menu_convert_audio_to_zx81_zoom_wave>=4) cada_cuanto_convert_zx81=1;

            int texto_destacar_posicion=0;

            int convert_audio_to_zx81_error_en_pos_actual=0;
            int convert_audio_to_zx81_error_en_pos_actual_pulsos=0;

            if (menu_convert_audio_to_zx81_window_running) {
                //Ubicar a la derecha de la ventana lo que se está convirtiendo
                int offset=x-xinicial_grafica-ancho_grafica;

                offset /=cada_cuanto_convert_zx81;

                offset *=menu_convert_audio_to_zx81_zoom_wave;

                offset+=menu_convert_audio_to_zx81_waveform_get_input_position();

                //Y ajustar offset a nivel de zoom, para que la señal no "cambie" a medida que va moviendose
                //Asi los valores medios a obtener siempre se empieza por un offset multiple del zoom
                int restar=offset % menu_convert_audio_to_zx81_zoom_wave;
                offset -=restar;

                //Ver si hay algun error en posicion actual
                convert_audio_to_zx81_error_en_pos_actual=menu_convert_audio_to_zx81_si_error_pos_actual(
                        offset,&convert_audio_to_zx81_error_en_pos_actual_pulsos);

                //destacar pulso, bit o byte
                if (menu_convert_audio_to_zx81_wave_follows_conversion) {
                    int posicion_color_destacar=menu_convert_audio_to_zx81_get_color_destacar();
                    if (offset>=posicion_color_destacar) {
                        color_linea=ESTILO_GUI_COLOR_BLOCK_VISUALTAPE;

                        if (offset==posicion_color_destacar) {
                            texto_destacar_posicion=1;
                        }
                    }
                }


                if (offset<0) {
                    valor_medio=0;
                    convert_zx81_max_valor=128;
                    convert_zx81_min_valor=128;
                }
                else {
                    //acceder a la memoria de input rwa

                    //generar valores medios segun zoom
                    int valor_unsigned=0;
                    int jj;

                    for (jj=0;jj<menu_convert_audio_to_zx81_zoom_wave;jj++) {
                        int valor_sample=menu_convert_audio_to_zx81_get_sample(offset+jj);
                        valor_unsigned +=valor_sample;

                        if (valor_sample>convert_zx81_max_valor) convert_zx81_max_valor=valor_sample;
                        if (valor_sample<convert_zx81_min_valor) convert_zx81_min_valor=valor_sample;
                    }

                    valor_unsigned /=menu_convert_audio_to_zx81_zoom_wave;

                    int valor_signed=valor_unsigned-128;

                    valor_medio=valor_signed;
                }

            }


			valor_audio=valor_medio;

			//Lo escalamos a maximo alto

			//y=valor_audio;
			y=valor_audio*alto/256;

			//Lo situamos en el centro. Negativo hacia abajo (Y positiva)
			y=menu_audio_draw_sound_wave_ycentro-y;


            int dibujar_linea=1;

			//unimos valor anterior con actual con una linea vertical
            //Pero si no es el primer valor
			if (x!=xinicial_grafica) {
				if (si_complete_video_driver() ) {

					//Onda no llena
					if (!menu_sound_wave_llena) {

                        //Para mostrar forma de onda de conversion de audio a zx81
                        if (menu_convert_audio_to_zx81_window_running) {
                            if (x % cada_cuanto_convert_zx81 != 0) dibujar_linea=0;

                            if (dibujar_linea) {
                                if (menu_convert_audio_to_zx81_zoom_wave>=16) {
                                    //maximo y minimo de ese trozo de zoom

                                    convert_zx81_max_valor=convert_zx81_max_valor-128;
                                    int convert_zx81_max_valor_y=convert_zx81_max_valor*alto/256;
                                    //Lo situamos en el centro. Negativo hacia abajo (Y positiva)
                                    convert_zx81_max_valor_y=menu_audio_draw_sound_wave_ycentro-convert_zx81_max_valor_y;

                                    convert_zx81_min_valor=convert_zx81_min_valor-128;
                                    int convert_zx81_min_valor_y=convert_zx81_min_valor*alto/256;
                                    //Lo situamos en el centro. Negativo hacia abajo (Y positiva)
                                    convert_zx81_min_valor_y=menu_audio_draw_sound_wave_ycentro-convert_zx81_min_valor_y;

                                    //Indicamos un color mas oscuro sobre la zona de maximos y minimos
                                    zxvision_draw_line(menu_audio_draw_sound_wave_window,
                                        x,convert_zx81_min_valor_y,
                                        x,convert_zx81_max_valor_y,ESTILO_GUI_COLOR_WAVEFORM_OSCURO,
                                        menu_waveform_putpixel_array_from_linea);
                                }


                                //unir el punto del sample anterior con el actual
                                zxvision_draw_line(menu_audio_draw_sound_wave_window,x-cada_cuanto_convert_zx81,lasty,x,y,color_linea,
                                                    menu_waveform_putpixel_array_from_linea);

                                if (y>destacar_max_y) destacar_max_y=y;

                                //Solo escribir este texto cuando zoom maximo
                                if (texto_destacar_posicion && menu_convert_audio_to_zx81_zoom_wave==1) {
                                    char buffer_texto_destacar[40];

                                    //Como el pulse tiene poca longitud, quitamos el texto Start, para que se pueda ver
                                    if (menu_convert_audio_to_zx81_que_destacamos_en_waveform==0) strcpy(buffer_texto_destacar,"Pulse");
                                    else sprintf(buffer_texto_destacar,"Start %s",menu_convert_audio_to_zx81_get_string_destacar() );

                                    /*
                                    //la coordenada y mas abajo de las dos, de la union de las dos lineas anteriores
                                    int baja_y=y;
                                    if (lasty>baja_y) baja_y=lasty;
                                    */

                                    zxvision_print_vectorial_text(menu_audio_draw_sound_wave_window,x,
                                        destacar_max_y+20,2,
                                        color_linea, buffer_texto_destacar,
                                        menu_waveform_putpixel_array_from_linea);
                                }

                                //Si error en pos actual
                                if (convert_audio_to_zx81_error_en_pos_actual && menu_convert_audio_to_zx81_zoom_wave==1) {
                                    char buffer_texto_pulsos[40];

                                    sprintf(buffer_texto_pulsos,"<-Error %d pulses!!",convert_audio_to_zx81_error_en_pos_actual_pulsos);

                                    //+40. algo mas abajo del texto a destacar, para que no se mezclen
                                    /*zxvision_print_vectorial_text(menu_audio_draw_sound_wave_window,x,
                                        destacar_max_y+40,2,
                                        ESTILO_GUI_COLOR_WAVEFORM, buffer_texto_pulsos,
                                        menu_waveform_putpixel_array_from_linea);*/

                                    //En coordenada Y 0 para no mezclar
                                    zxvision_print_vectorial_text(menu_audio_draw_sound_wave_window,x,
                                        0,2,
                                        ESTILO_GUI_COLOR_WAVEFORM, buffer_texto_pulsos,
                                        menu_waveform_putpixel_array_from_linea);
                                }



                            }
                        }

                        else {
                        zxvision_draw_line(menu_audio_draw_sound_wave_window,x,lasty,x,y,ESTILO_GUI_COLOR_WAVEFORM,
                            menu_waveform_putpixel_array_from_linea);
                        }
                    }

					//dibujar la onda "llena", es decir, siempre contar desde centro
					//el centro de la y de la onda es variable... se saca valor medio de todos los valores mostrados en pantalla
					//Onda llena
					else {
                        zxvision_draw_line(menu_audio_draw_sound_wave_window,x,audiomedio,x,y,ESTILO_GUI_COLOR_WAVEFORM,
                            menu_waveform_putpixel_array_from_linea);
                    }



				}
			}

            if (dibujar_linea) {
			lasty=y;


                //dibujamos valor actual
                if (si_complete_video_driver() ) {
                    menu_waveform_putpixel_array(x,y,ESTILO_GUI_COLOR_WAVEFORM);
                }

                else {
                    zxvision_print_char_simple(menu_audio_draw_sound_wave_window,xorigen+x,yorigen+y,ESTILO_GUI_TINTA_NORMAL,ESTILO_GUI_PAPEL_NORMAL,0,'#');
                }

            }

		}


        }

        if (menu_waveform_separar_canales.v) {
            //dibujar linea separación
            if (si_complete_video_driver() ) {
                zxvision_draw_line(menu_audio_draw_sound_wave_window,ancho/2,0,ancho/2,alto-1,ESTILO_GUI_TINTA_NORMAL,menu_waveform_putpixel_array_from_linea);
            }
        }

	}


	//Dibujar todo array de waveform en pantalla
    if (si_complete_video_driver() ) {
        menu_waveform_draw_array(ancho,alto,xorigen,yorigen,-1);
    }


	//printf ("%d ",puntero_audio);


	//Volume. Mostrarlo siempre, no solo dos veces por segundo, para que se actualice mas frecuentemente
	//if (menu_waveform_previous_volume<menu_audio_draw_sound_wave_volumen_escalado) menu_waveform_previous_volume=menu_audio_draw_sound_wave_volumen_escalado;
	menu_waveform_previous_volume=menu_decae_ajusta_valor_volumen(menu_waveform_previous_volume,menu_audio_draw_sound_wave_volumen_escalado);

	char texto_volumen[32];
    menu_string_volumen(texto_volumen,menu_audio_draw_sound_wave_volumen_escalado,menu_waveform_previous_volume);
                                                                //"Volume C: %s"

	sprintf (buffer_texto_medio,"Volume: %3d %s",menu_audio_draw_sound_wave_volumen,texto_volumen);

	zxvision_print_string_defaults_fillspc(menu_audio_draw_sound_wave_window,1,2,buffer_texto_medio);


	zxvision_draw_window_contents(menu_audio_draw_sound_wave_window);

}




void menu_audio_new_waveform_shape(MENU_ITEM_PARAMETERS)
{
	//menu_sound_wave_llena ^=1;

	menu_sound_wave_llena++;
	if (menu_sound_wave_llena==3) menu_sound_wave_llena=0;

	//Modo scroll no se permite si no hay video driver completo
	if (!si_complete_video_driver() && menu_sound_wave_llena==2) menu_sound_wave_llena=0;
}

void menu_audio_waveform_sep_canales_setting(MENU_ITEM_PARAMETERS)
{
    menu_waveform_separar_canales.v ^=1;
}

zxvision_window zxvision_window_audio_waveform;

void menu_audio_new_waveform(MENU_ITEM_PARAMETERS)
{

    //printf("Inicio waveform\n");

 	menu_espera_no_tecla();
	menu_reset_counters_tecla_repeticion();

    //printf("Inicio 2 waveform\n");

	//zxvision_window ventana;

		zxvision_window *ventana;
		ventana=&zxvision_window_audio_waveform;


	//IMPORTANTE! no crear ventana si ya existe. Esto hay que hacerlo en todas las ventanas que permiten background.
	//si no se hiciera, se crearia la misma ventana, y en la lista de ventanas activas , al redibujarse,
	//la primera ventana repetida apuntaria a la segunda, que es el mismo puntero, y redibujaria la misma, y se quedaria en bucle colgado
	//zxvision_delete_window_if_exists(ventana);

    //Crear ventana si no existe
    if (!zxvision_if_window_already_exists(ventana)) {

        int x,y,ancho,alto,is_minimized,is_maximized,ancho_antes_minimize,alto_antes_minimize;

        if (!util_find_window_geometry("waveform",&x,&y,&ancho,&alto,&is_minimized,&is_maximized,&ancho_antes_minimize,&alto_antes_minimize)) {
            //x=SOUND_WAVE_X;
            //y=SOUND_WAVE_Y-2;
            ancho=SOUND_WAVE_ANCHO;
            alto=SOUND_WAVE_ALTO+4;

            x=menu_center_x()-ancho/2;
            y=menu_center_y()-alto/2;
        }


        //Crear nueva ventana, asignando ademas geometry name y gestionando si se crea minimizada
        zxvision_new_window_gn_cim(ventana,x,y,ancho,alto,ancho-1,alto-2,"Waveform","waveform",is_minimized,is_maximized,ancho_antes_minimize,alto_antes_minimize);


        //zxvision_new_window_nocheck_staticsize(ventana,x,y,ancho,alto,ancho-1,alto-2,"Waveform");

        ventana->can_be_backgrounded=1;
        //indicar nombre del grabado de geometria
        //strcpy(ventana->geometry_name,"waveform");
        //restaurar estado minimizado de ventana
        //ventana->is_minimized=is_minimized;

        //decimos que tiene que borrar fondo cada vez al redibujar
        //por tanto es como decirle que no use cache de putchar
        //dado que el fondo de texto es casi todo texto con caracter " " eso borra los pixeles que metemos con overlay del frame anterior
        //77% cpu con esto a 1
        //37% cpu con esto a 0
        //42% cpu con esto a 0 y borrando pixeles anteriores
        //ventana->must_clear_cache_on_draw=1;

    }

    //Si ya existe, activar esta ventana
    else {
        zxvision_activate_this_window(ventana);
    }

	//printf("despues zxvision_new_window_nocheck_staticsize\n");
	zxvision_draw_window(ventana);


    //Cambiamos funcion overlay de texto de menu
    //Se establece a la de funcion de audio waveform
    //cambio overlay
    zxvision_set_window_overlay(ventana,menu_audio_draw_sound_wave);

	menu_audio_draw_sound_wave_window=ventana; //Decimos que el overlay lo hace sobre la ventana que tenemos aqui

	//Toda ventana que este listada en zxvision_known_window_names_array debe permitir poder salir desde aqui
	//Se sale despues de haber inicializado overlay y de cualquier otra variable que necesite el overlay
	if (zxvision_currently_restoring_windows_on_start) {
		//printf ("Saliendo de ventana ya que la estamos restaurando en startup\n");
		return;
	}

	menu_item *array_menu_audio_new_waveform;
	menu_item item_seleccionado;
	int retorno_menu;
	do {

        //borrar primera linea, por si antes hay visible opcion de view stereo/mono
        zxvision_print_string_defaults_fillspc(ventana,1,0,"");

		//Agrego dos espacios al final para borrar restos de "Scroll" pues ocupa 2 caracteres mas que "Line" y "Fill"
		char *tipos_soundwave[3]={" Line "," Fill ","Scroll"};

		menu_add_item_menu_inicial_format(&array_menu_audio_new_waveform,MENU_OPCION_NORMAL,menu_audio_new_waveform_shape,NULL,"[%s] ~~Shape",
				(tipos_soundwave[menu_sound_wave_llena]) );
		menu_add_item_menu_shortcut(array_menu_audio_new_waveform,'s');




		//Evito tooltips en los menus tabulados que tienen overlay porque al salir el tooltip detiene el overlay
		//menu_add_item_menu_tooltip(array_menu_audio_new_waveform,"Change wave Shape");
		menu_add_item_menu_ayuda(array_menu_audio_new_waveform,"Change wave Shape: simple line or vertical fill");

		menu_add_item_menu_tabulado(array_menu_audio_new_waveform,1,0);


        //Vista stereo solo para line y fill, no con scroll
        if (menu_sound_wave_llena<2) {
            menu_add_item_menu_format(array_menu_audio_new_waveform,MENU_OPCION_NORMAL,menu_audio_waveform_sep_canales_setting,NULL,"[%s] ~~View",
                (menu_waveform_separar_canales.v ? "Stereo" : " Mono "));
            menu_add_item_menu_shortcut(array_menu_audio_new_waveform,'v');

            menu_add_item_menu_tabulado(array_menu_audio_new_waveform,16,0);
        }


		//Nombre de ventana solo aparece en el caso de stdout
		retorno_menu=menu_dibuja_menu_no_title_lang(&audio_new_waveform_opcion_seleccionada,&item_seleccionado,array_menu_audio_new_waveform,"Waveform" );

		if (retorno_menu!=MENU_RETORNO_BACKGROUND) {


			//En caso de menus tabulados, es responsabilidad de este de borrar la ventana

			if ((item_seleccionado.tipo_opcion&MENU_OPCION_ESC)==0 && retorno_menu>=0) {
				//llamamos por valor de funcion
				if (item_seleccionado.menu_funcion!=NULL) {
					//printf ("actuamos por funcion\n");
					item_seleccionado.menu_funcion(item_seleccionado.valor_opcion);


				}
			}
		}

	} while ( (item_seleccionado.tipo_opcion&MENU_OPCION_ESC)==0 && retorno_menu!=MENU_RETORNO_ESC && !salir_todos_menus && retorno_menu!=MENU_RETORNO_BACKGROUND);


    //En caso de menus tabulados, suele ser necesario esto. Si no, la ventana se quedaria visible


	//Grabar geometria ventana
	util_add_window_geometry_compact(ventana);


	if (retorno_menu==MENU_RETORNO_BACKGROUND) {
                zxvision_message_put_window_background();
	}

	else {
		//En caso de menus tabulados, es responsabilidad de este de liberar ventana
		zxvision_destroy_window(ventana);
	}

}


zxvision_window *menu_debug_draw_visualmem_window;



#ifdef EMULATE_VISUALMEM


//#define visualmem_ancho_variable (menu_debug_draw_visualmem_window->visible_width-1)
//#define visualmem_alto_variable (menu_debug_draw_visualmem_window->visible_height-1)

#define VISUALMEM_MIN_X (menu_origin_x())
#define VISUALMEM_MIN_Y 0

#define VISUALMEM_DEFAULT_X (VISUALMEM_MIN_X+1)

//int visualmem_x_variable=VISUALMEM_DEFAULT_X;

#define VISUALMEM_DEFAULT_Y (VISUALMEM_MIN_Y+1)
int visualmem_y_variable=VISUALMEM_DEFAULT_Y;

#define VISUALMEM_ANCHO (menu_debug_draw_visualmem_window->visible_width)
#define VISUALMEM_ALTO (menu_debug_draw_visualmem_window->visible_height)

#define VISUALMEM_DEFAULT_WINDOW_ANCHO 30
#define VISUALMEM_DEFAULT_WINDOW_ALTO 22

//0=vemos visualmem write
//1=vemos visualmem read
//2=vemos visualmem opcode
//3=vemos visualmem write+read+opcode todos a la vez
//4=vemos mmc write
//5=vemos mmc read
//6=vemos mmc write+read
//7=vemos HiLow write
//8=vemos HiLow read
//9=vemos HiLow write+read
//10=vemos Microdrive write
//11=vemos Microdrive read
//12=vemos Microdrive write+read
int menu_visualmem_donde=0;

int menu_visualmem_modo_defrag=1;


int visualmem_bright_multiplier=10;


//Dice si se muestra visualmem grafico o de texto.
//Si es un driver de solo texto, mostrar texto
//Si es un driver grafico y setting dice que lo mostremos en texto, mostrar texto
//Si nada de lo demas, mostrar grafico
int si_mostrar_visualmem_grafico(void)
{
	if (!si_complete_video_driver()) return 0;

	if (!setting_mostrar_visualmem_grafico.v) return 0;

	return 1;

}

//Dice inicio y final de visualmem
void menu_visualmem_get_start_end(int *inicio,int *final)
{
    int inicio_puntero_membuffer,final_puntero_membuffer;

	//Por defecto
	inicio_puntero_membuffer=16384;
	final_puntero_membuffer=65536;

	//printf ("ancho: %d alto: %d\n",ancho,alto);

	//En spectrum 16kb
	if (MACHINE_IS_SPECTRUM_16) {
		//printf ("spec 16kb\n");
		final_puntero_membuffer=32768;
	}

	if (MACHINE_IS_Z88) {
		        inicio_puntero_membuffer=0;
	}

	//En Inves, mostramos modificaciones a la RAM baja
	if (MACHINE_IS_INVES) {
                        inicio_puntero_membuffer=0;
        }



	//En zx80, zx81 mostrar desde 8192 por si tenemos expansion packs
	if (MACHINE_IS_ZX8081) {
		//por defecto
		//printf ("ramtop_zx8081: %d\n",ramtop_zx8081);
		final_puntero_membuffer=ramtop_zx8081+1;

		if (ram_in_8192.v) {
			//printf ("memoria en 8192\n");
			inicio_puntero_membuffer=8192;
		}

        	if (ram_in_32768.v) {
			//printf ("memoria en 32768\n");
			final_puntero_membuffer=49152;
		}

	        if (ram_in_49152.v) {
			//printf ("memoria en 49152\n");
			final_puntero_membuffer=65536;
		}

	}

        //En Jupiter Ace, desde 8192
        if (MACHINE_IS_ACE) {
                //por defecto
                final_puntero_membuffer=ramtop_ace+1;
                inicio_puntero_membuffer=8192;

        }


	//En CPC, desde 0
	if (MACHINE_IS_CPC) {
		inicio_puntero_membuffer=0;
	}

	if (MACHINE_IS_PCW) {
		inicio_puntero_membuffer=0;
	}

	if (MACHINE_IS_SAM) {
                inicio_puntero_membuffer=0;
        }



	//En modos de RAM en ROM de +2a en puntero apuntara a direccion 0
	if (MACHINE_IS_SPECTRUM_P2A_P3) {
		if ( (puerto_32765 & 32) == 0 ) {
			//paginacion habilitada

			if ( (puerto_8189 & 1) ) {
				//paginacion de ram en rom
				//printf ("paginacion de ram en rom\n");
				inicio_puntero_membuffer=0;
			}
		}
	}

	if (MACHINE_IS_QL) {
		//inicio_puntero_membuffer=0x18000;
		//la ram propiamente empieza en 20000H
		inicio_puntero_membuffer=0x20000;
		final_puntero_membuffer=ql_mem_limit+1;
	}

	//Si es de opcode o read, puede ser desde cualquier sitio desde la rom
	if (menu_visualmem_donde>0) {
		inicio_puntero_membuffer=0;
	}

	if (menu_visualmem_donde==4 || menu_visualmem_donde==5 || menu_visualmem_donde==6) {
		final_puntero_membuffer=VISUALMEM_MMC_BUFFER_SIZE;
	}

	if (menu_visualmem_donde==7 || menu_visualmem_donde==8 || menu_visualmem_donde==9) {
		final_puntero_membuffer=VISUALMEM_HILOW_BUFFER_SIZE;
	}

	if (menu_visualmem_donde==10 || menu_visualmem_donde==11 || menu_visualmem_donde==12) {
        //microdrive. Si raw, es mas grande
        int esraw=0;
        int microdrive_activo=microdrive_primer_motor_activo();
        //Si no hay ninguno activo, asumimos el primero
        if (microdrive_activo<0) esraw=microdrive_status[0].raw_format;

        else esraw=microdrive_status[microdrive_activo].raw_format;


        if (esraw) {
            final_puntero_membuffer=VISUALMEM_MICRODRIVE_ASSIGNED_BUFFER_SIZE;
        }
		else final_puntero_membuffer=VISUALMEM_MICRODRIVE_BUFFER_SIZE;
	}


	*inicio=inicio_puntero_membuffer;
	*final=final_puntero_membuffer;
}

void menu_visualmem_get_accumulated_value(int puntero,int *acumulado,int *acumulado_written,int *acumulado_read,int *acumulado_opcode)
{

	//printf ("puntero: %d\n",inicio_puntero_membuffer);

	//0: written, 1: read, 2: opcode, 3: read+write+opcode, 4: read mmc, 5: write mmc

	switch(menu_visualmem_donde) {
		case 0:
			*acumulado +=visualmem_buffer[puntero];
			clear_visualmembuffer(puntero);
		break;

		case 1:
			*acumulado +=visualmem_read_buffer[puntero];
			clear_visualmemreadbuffer(puntero);
		break;

		case 2:
			*acumulado +=visualmem_opcode_buffer[puntero];
			clear_visualmemopcodebuffer(puntero);
		break;

		case 3:
			*acumulado_written +=visualmem_buffer[puntero];
			*acumulado_read +=visualmem_read_buffer[puntero];
			*acumulado_opcode +=visualmem_opcode_buffer[puntero];
			clear_visualmembuffer(puntero);
			clear_visualmemreadbuffer(puntero);
			clear_visualmemopcodebuffer(puntero);
		break;

		case 4:
			*acumulado +=visualmem_mmc_write_buffer[puntero];
			clear_visualmemmmc_write_buffer(puntero);
		break;

		case 5:
			*acumulado +=visualmem_mmc_read_buffer[puntero];
			clear_visualmemmmc_read_buffer(puntero);
		break;

		case 6:
			*acumulado_written +=visualmem_mmc_write_buffer[puntero];
			*acumulado_read +=visualmem_mmc_read_buffer[puntero];
			clear_visualmemmmc_write_buffer(puntero);
			clear_visualmemmmc_read_buffer(puntero);
		break;

		case 7:
			*acumulado +=visualmem_hilow_write_buffer[puntero];
			clear_visualmemhilow_write_buffer(puntero);
		break;

		case 8:
			*acumulado +=visualmem_hilow_read_buffer[puntero];
			clear_visualmemhilow_read_buffer(puntero);
		break;

		case 9:
			*acumulado_written +=visualmem_hilow_write_buffer[puntero];
			*acumulado_read +=visualmem_hilow_read_buffer[puntero];
			clear_visualmemhilow_write_buffer(puntero);
			clear_visualmemhilow_read_buffer(puntero);
		break;

		case 10:
			*acumulado +=visualmem_microdrive_write_buffer[puntero];
			clear_visualmemmicrodrive_write_buffer(puntero);
		break;

		case 11:
			*acumulado +=visualmem_microdrive_read_buffer[puntero];
			clear_visualmemmicrodrive_read_buffer(puntero);
		break;

		case 12:
			*acumulado_written +=visualmem_microdrive_write_buffer[puntero];
			*acumulado_read +=visualmem_microdrive_read_buffer[puntero];
			clear_visualmemmicrodrive_write_buffer(puntero);
			clear_visualmemmicrodrive_read_buffer(puntero);
		break;


	}


}

void menu_visualmem_putpixel(zxvision_window *ventana,int x,int y,int color_pixel,int color_tinta,int color_papel,z80_byte caracter)
{


				if (si_mostrar_visualmem_grafico() ) {
					if (menu_visualmem_modo_defrag) {
						//Cuadradito de color
						int x2,y2;
						for (y2=0;y2<4;y2++) {
							for (x2=0;x2<4;x2++) {
								int color_final=color_pixel;
								if (x2==3 || y2==3) color_final=ESTILO_GUI_PAPEL_NORMAL;

								//if (x2==0 && x<30) printf("%d %d\n",x,x*4+x2);

								zxvision_putpixel(ventana,x*4+x2,y*4+y2,color_final);
							}
						}

					}
					else zxvision_putpixel(ventana,x,y,color_pixel);
				}
				else {
					zxvision_print_char_simple(ventana,x,y,color_tinta,color_papel,0,caracter);
				}


}

void menu_debug_draw_visualmem(void)
{




    //si ventana minimizada, no ejecutar todo el codigo de overlay
    if (menu_debug_draw_visualmem_window->is_minimized) return;


	int ancho=(VISUALMEM_ANCHO-2);
	int alto=(VISUALMEM_ALTO-5);

	if (ancho<1 || alto<1) return;

	int xorigen=1;
	int yorigen=3;



	if (si_mostrar_visualmem_grafico() ) {

		int multiplicar_ancho=menu_char_width;
		int multiplicar_alto=8;

		if (menu_visualmem_modo_defrag) {

			//Original
			//multiplicar_ancho /=4;

			//Para que con char width =8 resulte /2
			//multiplicar_ancho /=(menu_char_width/2);
			//A/(A/2)=A/1  / A/2 = A*2 /A=2 -> simplificado
			//Nuevo calculo
			//multiplicar_ancho=2;



			//Antiguo calculo
			//TODO: ajustes segun char size 7,6,5 mejor que lo que hay ahora
			//multiplicar_ancho /=4;


			//ancho *=multiplicar_ancho;
			//en modo defrag cada cuadradito son 4 pixeles de anchoXalto


			ancho=((ancho*multiplicar_ancho)/4);  //-5 //Quitamos 5 para dar margen por la derecha

			multiplicar_alto /=4;
			alto *=multiplicar_alto;

			//No alterar xorigen. Para que no quede tanto margen por la derecha
			//xorigen *=multiplicar_ancho;
			yorigen *=multiplicar_alto;
		}

		else {

			ancho *=multiplicar_ancho;
			alto *=multiplicar_alto;

			xorigen *=multiplicar_ancho;
			yorigen *=multiplicar_alto;
		}


	}


	int tamanyo_total=ancho*alto;

	int x,y;


	int inicio_puntero_membuffer,final_puntero_membuffer;

	menu_visualmem_get_start_end(&inicio_puntero_membuffer,&final_puntero_membuffer);

	//Valores entre 0 y 255: numero de veces byte modificado
	//Valor 65535 especial


	//Calcular cuantos bytes modificados representa un pixel, teniendo en cuenta maximo buffer
	int max_valores=(final_puntero_membuffer-inicio_puntero_membuffer)/tamanyo_total;

	//printf ("max_valores: %d\n",max_valores);
	//printf ("tamanyo total: %d\n",tamanyo_total);
	//le damos uno mas para poder llenar la ventana
	//printf ("inicio: %06XH final: %06XH\n",inicio_puntero_membuffer,final_puntero_membuffer);
	max_valores++;

	for (y=yorigen;y<yorigen+alto;y++) {
        for (x=xorigen;x<xorigen+ancho;x++) {

			//Obtenemos conjunto de bytes modificados

			int valores=max_valores;

			int acumulado=0;

			int acumulado_written,acumulado_read,acumulado_opcode;
			acumulado_written=acumulado_read=acumulado_opcode=0; //Estos usados al visualizar los 3 a la vez

			//si_modificado=0;
			for (;valores>0;valores--,inicio_puntero_membuffer++) {
				if (inicio_puntero_membuffer>=final_puntero_membuffer) {
					//printf ("llegado a final con x: %d y: %d ",x,y);
					//Fuera de memoria direccionable. Zona gris. Decrementamos valor
					//Como se lee a trozos de "max_valores" tamanyo, cuando este trozo empieza ya fuera de memoria
					//acumulado acabara siendo <0 y saldra gris. Si es a medias, si acaba restando mucho, saldra gris tambien
					//(eso solo pasara en el ultimo pixel de la zona direccionable)
					acumulado--;
				}
				else {
					//Es en memoria direccionable. Sumar valor de visualmem y luego haremos valor medio
					menu_visualmem_get_accumulated_value(inicio_puntero_membuffer,&acumulado,&acumulado_written,&acumulado_read,&acumulado_opcode);
				}
       		}

			//dibujamos valor medio
			if (acumulado>0 || acumulado_written>0 || acumulado_read>0 || acumulado_opcode>0) {

				//Sacar valor medio
				int color_final=acumulado/max_valores;

				if (si_mostrar_visualmem_grafico() ) {

					//Solo calcular esto si tenemos driver grafico completo


					//int color_final=acumulado/max_valores;

					//printf ("color final: %d\n",color_final);

					//Aumentar el brillo del color
					color_final=color_final*visualmem_bright_multiplier;
					if (color_final>255) color_final=255;


					if (menu_visualmem_donde==3) {
						//Los 3 de ram a la vez. Combinamos color RGB sacando color de paleta tsconf (15 bits)
						//Paleta es RGB R: 5 bits altos, G: 5 bits medios, B:5 bits bajos


						//Sacar valor medio de los 3 componentes
						int color_final_written=acumulado_written/max_valores;
						color_final_written=color_final_written*visualmem_bright_multiplier;
						if (color_final_written>31) color_final_written=31;

						int color_final_read=acumulado_read/max_valores;
						color_final_read=color_final_read*visualmem_bright_multiplier;
						if (color_final_read>31) color_final_read=31;

						int color_final_opcode=acumulado_opcode/max_valores;
						color_final_opcode=color_final_opcode*visualmem_bright_multiplier;
						if (color_final_opcode>31) color_final_opcode=31;

						//Blue sera para los written
						//Green sera para los read
						//Red sera para los opcode
						color_final=(color_final_opcode<<10)|(color_final_read<<5)|color_final_written;

						color_final +=TSCONF_INDEX_FIRST_COLOR;

					}

					else if (menu_visualmem_donde==6) {
						//Los 2 de MMC a la vez. Combinamos color RGB sacando color de paleta tsconf (15 bits)
						//Paleta es RGB R: 5 bits altos, G: 5 bits medios, B:5 bits bajos


						//Sacar valor medio de los 2 componentes
						int color_final_written=acumulado_written/max_valores;
						color_final_written=color_final_written*visualmem_bright_multiplier;
						if (color_final_written>31) color_final_written=31;

						int color_final_read=acumulado_read/max_valores;
						color_final_read=color_final_read*visualmem_bright_multiplier;
						if (color_final_read>31) color_final_read=31;

						//Blue sera para los written
						//Green sera para los read

						color_final=(color_final_read<<5)|color_final_written;

						color_final +=TSCONF_INDEX_FIRST_COLOR;

					}


					else if (menu_visualmem_donde==9) {
						//Los 2 de hilow a la vez. Combinamos color RGB sacando color de paleta tsconf (15 bits)
						//Paleta es RGB R: 5 bits altos, G: 5 bits medios, B:5 bits bajos


						//Sacar valor medio de los 2 componentes
						int color_final_written=acumulado_written/max_valores;
						color_final_written=color_final_written*visualmem_bright_multiplier;
						if (color_final_written>31) color_final_written=31;

						int color_final_read=acumulado_read/max_valores;
						color_final_read=color_final_read*visualmem_bright_multiplier;
						if (color_final_read>31) color_final_read=31;

						//Blue sera para los written
						//Green sera para los read

						color_final=(color_final_read<<5)|color_final_written;

						color_final +=TSCONF_INDEX_FIRST_COLOR;

					}

					else if (menu_visualmem_donde==12) {
						//Los 2 de microdrive a la vez. Combinamos color RGB sacando color de paleta tsconf (15 bits)
						//Paleta es RGB R: 5 bits altos, G: 5 bits medios, B:5 bits bajos


						//Sacar valor medio de los 2 componentes
						int color_final_written=acumulado_written/max_valores;
						color_final_written=color_final_written*visualmem_bright_multiplier;
						if (color_final_written>31) color_final_written=31;

						int color_final_read=acumulado_read/max_valores;
						color_final_read=color_final_read*visualmem_bright_multiplier;
						if (color_final_read>31) color_final_read=31;

						//Blue sera para los written
						//Green sera para los read

						color_final=(color_final_read<<5)|color_final_written;

						color_final +=TSCONF_INDEX_FIRST_COLOR;

					}

					else {
						color_final +=HEATMAP_INDEX_FIRST_COLOR;
					}

				}

				menu_visualmem_putpixel(menu_debug_draw_visualmem_window,x,y,color_final,ESTILO_GUI_TINTA_NORMAL,ESTILO_GUI_PAPEL_NORMAL,'#');
			}



			//color ficticio para indicar fuera de memoria y por tanto final de ventana... para saber donde acaba
			else if (acumulado<0) {
				if (menu_visualmem_modo_defrag) {
					//blancos
					menu_visualmem_putpixel(menu_debug_draw_visualmem_window,x,y,ESTILO_GUI_PAPEL_NORMAL,ESTILO_GUI_TINTA_NORMAL,ESTILO_GUI_PAPEL_NORMAL,'-');
				}
				else {
					menu_visualmem_putpixel(menu_debug_draw_visualmem_window,x,y,ESTILO_GUI_COLOR_UNUSED_VISUALMEM,ESTILO_GUI_TINTA_NORMAL,ESTILO_GUI_PAPEL_NORMAL,'-');
				}
			}

			//Valor 0
			else {
				if (menu_visualmem_modo_defrag) {
					//En este caso los cuadraditos de fondo se ven tambien
					menu_visualmem_putpixel(menu_debug_draw_visualmem_window,x,y,ESTILO_GUI_COLOR_UNUSED_VISUALMEM,ESTILO_GUI_TINTA_NORMAL,ESTILO_GUI_PAPEL_NORMAL,' ');
				}

				else {
					menu_visualmem_putpixel(menu_debug_draw_visualmem_window,x,y,ESTILO_GUI_PAPEL_NORMAL,ESTILO_GUI_TINTA_NORMAL,ESTILO_GUI_PAPEL_NORMAL,' ');
				}

			}

	    }
	}

	zxvision_draw_window_contents(menu_debug_draw_visualmem_window);

}

#define VISUALMEM_TOTAL_TYPES 13

char *visualmem_types_names[]={
		"MEM Write",   //0
		"MEM Read",
		"Opcode",
		"MEM W+R+Opcode",
		"MMC Write",
		"MMC Read",
		"MMC Write+Read",
		"HiLow Write",
		"HiLow Read",
		"HiLow Write+Read",
		"Microdrive Write", //10
		"Microdrive Read",
		"Microdrive Write+Read"
};

void menu_visualmem_change_type_set(MENU_ITEM_PARAMETERS)
{
    menu_visualmem_donde=valor_opcion;

}



void menu_visualmem_change_type(MENU_ITEM_PARAMETERS)
{
    menu_item *array_menu_common;
    menu_item item_seleccionado;
    int retorno_menu;

    int opcion_seleccionada=menu_visualmem_donde;

    //No reintentar. seleccionar uno y salir
    //do {

        menu_add_item_menu_inicial(&array_menu_common,"",MENU_OPCION_UNASSIGNED,NULL,NULL);

        int i;

        for (i=0;i<VISUALMEM_TOTAL_TYPES;i++) {
            menu_add_item_menu_format(array_menu_common,MENU_OPCION_NORMAL,menu_visualmem_change_type_set,NULL,"%s",visualmem_types_names[i]);
            menu_add_item_menu_valor_opcion(array_menu_common,i);
        }


        menu_add_item_menu_separator(array_menu_common);

        menu_add_ESC_item(array_menu_common);


        retorno_menu=menu_dibuja_menu_dialogo_no_title_lang(&opcion_seleccionada,&item_seleccionado,array_menu_common,
            "Visualmem Type");

        if ((item_seleccionado.tipo_opcion&MENU_OPCION_ESC)==0 && retorno_menu>=0) {
            //llamamos por valor de funcion
            if (item_seleccionado.menu_funcion!=NULL) {
                //printf ("actuamos por funcion\n");
                item_seleccionado.menu_funcion(item_seleccionado.valor_opcion);

            }
        }

    //} while ( (item_seleccionado.tipo_opcion&MENU_OPCION_ESC)==0 && retorno_menu!=MENU_RETORNO_ESC && !salir_todos_menus);

}

void menu_debug_new_visualmem_defrag_mode(MENU_ITEM_PARAMETERS)
{
	menu_visualmem_modo_defrag ^=1;
}

void menu_debug_new_visualmem_bright(MENU_ITEM_PARAMETERS)
{
	if (visualmem_bright_multiplier>=200) visualmem_bright_multiplier=1;
	else if (visualmem_bright_multiplier==1) visualmem_bright_multiplier=10;
	else visualmem_bright_multiplier +=10;
}


zxvision_window zxvision_window_visualmem;

void menu_debug_new_visualmem(MENU_ITEM_PARAMETERS)
{


 	menu_espera_no_tecla();
	menu_reset_counters_tecla_repeticion();

	zxvision_window *ventana;
	ventana=&zxvision_window_visualmem;


	//IMPORTANTE! no crear ventana si ya existe. Esto hay que hacerlo en todas las ventanas que permiten background.
	//si no se hiciera, se crearia la misma ventana, y en la lista de ventanas activas , al redibujarse,
	//la primera ventana repetida apuntaria a la segunda, que es el mismo puntero, y redibujaria la misma, y se quedaria en bucle colgado
	//zxvision_delete_window_if_exists(ventana);

    //Crear ventana si no existe
    if (!zxvision_if_window_already_exists(ventana)) {

        int x,y,ancho,alto,is_minimized,is_maximized,ancho_antes_minimize,alto_antes_minimize;

        if (!util_find_window_geometry("visualmem",&x,&y,&ancho,&alto,&is_minimized,&is_maximized,&ancho_antes_minimize,&alto_antes_minimize)) {
            //x=VISUALMEM_DEFAULT_X;
            //y=visualmem_y_variable;
            ancho=VISUALMEM_DEFAULT_WINDOW_ANCHO;
            alto=VISUALMEM_DEFAULT_WINDOW_ALTO;

            x=menu_center_x()-ancho/2;
            y=menu_center_y()-alto/2;
        }

        //Crear nueva ventana, asignando ademas geometry name y gestionando si se crea minimizada
        zxvision_new_window_gn_cim(ventana,x,y,ancho,alto,ancho-1,alto-2,"Visual memory","visualmem",is_minimized,is_maximized,ancho_antes_minimize,alto_antes_minimize);


        //zxvision_new_window_nocheck_staticsize(ventana,x,y,ancho,alto,ancho-1,alto-2,"Visual memory");

        ventana->can_be_backgrounded=1;
        //indicar nombre del grabado de geometria
        //strcpy(ventana->geometry_name,"visualmem");
        //restaurar estado minimizado de ventana
        //ventana->is_minimized=is_minimized;

    }

    //Si ya existe, activar esta ventana
    else {
        zxvision_activate_this_window(ventana);
    }

	zxvision_draw_window(ventana);


    menu_debug_draw_visualmem_window=ventana; //Decimos que el overlay lo hace sobre la ventana que tenemos aqui

	//Cambiamos funcion overlay de texto de menu
	//Se establece a la de funcion de visualmem + texto
    //cambio overlay
    zxvision_set_window_overlay(ventana,menu_debug_draw_visualmem);




       //Toda ventana que este listada en zxvision_known_window_names_array debe permitir poder salir desde aqui
       //Se sale despues de haber inicializado overlay y de cualquier otra variable que necesite el overlay
       if (zxvision_currently_restoring_windows_on_start) {
               //printf ("Saliendo de ventana ya que la estamos restaurando en startup\n");
               return;
       }


	menu_dibuja_menu_permite_repeticiones_hotk=1;


	menu_item *array_menu_debug_new_visualmem;
	menu_item item_seleccionado;
	int retorno_menu;
	do {



		//Borrar las dos lineas donde van las opciones, por si hay "rastro" anterior
		zxvision_print_string_defaults_fillspc(ventana,1,0,"");
		zxvision_print_string_defaults_fillspc(ventana,1,1,"");


		menu_add_item_menu_inicial_format(&array_menu_debug_new_visualmem,MENU_OPCION_NORMAL,menu_debug_new_visualmem_bright,NULL,"~~Bright: %d",visualmem_bright_multiplier);
		menu_add_item_menu_shortcut(array_menu_debug_new_visualmem,'b');
		menu_add_item_menu_ayuda(array_menu_debug_new_visualmem,"Change bright value");
		menu_add_item_menu_tabulado(array_menu_debug_new_visualmem,1,0);


		//menu_debug_new_visualmem_defrag_mode
		if (si_mostrar_visualmem_grafico() ) {
			menu_add_item_menu_format(array_menu_debug_new_visualmem,MENU_OPCION_NORMAL,menu_debug_new_visualmem_defrag_mode,NULL,"[%c] ~~Defrag style",
		    							(menu_visualmem_modo_defrag ? 'X' : ' ' ));
			menu_add_item_menu_shortcut(array_menu_debug_new_visualmem,'d');
			menu_add_item_menu_ayuda(array_menu_debug_new_visualmem,"Defrag mode");
			menu_add_item_menu_tabulado(array_menu_debug_new_visualmem,14,0);
		}



		menu_add_item_menu_format(array_menu_debug_new_visualmem,MENU_OPCION_NORMAL,menu_visualmem_change_type,NULL,"~~Looking: %s",visualmem_types_names[menu_visualmem_donde]);
		menu_add_item_menu_shortcut(array_menu_debug_new_visualmem,'l');

		menu_add_item_menu_ayuda(array_menu_debug_new_visualmem,"Which visualmem to look at.\n\nIf you select MEM W+R+Opcode, the final color will be a RGB color result of:\n\n"
					"-Blue component por Written Mem\n-Green component for Read Mem\n-Red component for Opcode.\n\n"
					"Yellow for example is red+green, so opcode fetch+read memory. As an opcode fetch implies a read access,"
					" you won't ever see a red pixel (only opcode fetch) but all opcode fetch will always be yellow.\n"
					"Cyan is green+blue, so read+write\n\n"
					"If you select MMC Write+Read, the final color will be a RGB color result of:\n"
					"Blue component por Written MMC\nGreen component for Read MMC.\n"
					"Cyan for example is blue+green, so read+write MMC\n\n"

					);
		menu_add_item_menu_tabulado(array_menu_debug_new_visualmem,1,1);



		//Nombre de ventana solo aparece en el caso de stdout
		retorno_menu=menu_dibuja_menu_no_title_lang(&debug_new_visualmem_opcion_seleccionada,&item_seleccionado,array_menu_debug_new_visualmem,"Visual memory" );

		if (retorno_menu!=MENU_RETORNO_BACKGROUND) {
            //En caso de menus tabulados, es responsabilidad de este de borrar la ventana
            //Con este cls provoca que se borren todas las otras ventanas en background


            if ((item_seleccionado.tipo_opcion&MENU_OPCION_ESC)==0 && retorno_menu>=0) {
                    //llamamos por valor de funcion
                    if (item_seleccionado.menu_funcion!=NULL) {
                            //printf ("actuamos por funcion\n");
                            item_seleccionado.menu_funcion(item_seleccionado.valor_opcion);


                    }
            }
		}

	} while ( (item_seleccionado.tipo_opcion&MENU_OPCION_ESC)==0 && retorno_menu!=MENU_RETORNO_ESC && !salir_todos_menus && retorno_menu!=MENU_RETORNO_BACKGROUND);



	menu_dibuja_menu_permite_repeticiones_hotk=0;



    //En caso de menus tabulados, suele ser necesario esto. Si no, la ventana se quedaria visible


	util_add_window_geometry_compact(ventana);

	if (retorno_menu==MENU_RETORNO_BACKGROUND) {
                zxvision_message_put_window_background();
	}

	else {



		//En caso de menus tabulados, es responsabilidad de este de liberar ventana
		zxvision_destroy_window(ventana);
	}


}





#endif




void menu_ay_player_load(MENU_ITEM_PARAMETERS)
{
	char *filtros[2];

	filtros[0]="ay";

	filtros[1]=0;

	//guardamos directorio actual
	char directorio_actual[PATH_MAX];
	getcwd(directorio_actual,PATH_MAX);

	//Obtenemos directorio de ultimo archivo
	//si no hay directorio, vamos a rutas predefinidas
	if (last_ay_file[0]==0) menu_chdir_sharedfiles();

	else {
					char directorio[PATH_MAX];
					util_get_dir(last_ay_file,directorio);
					//printf ("strlen directorio: %d directorio: %s\n",strlen(directorio),directorio);

					//cambiamos a ese directorio, siempre que no sea nulo
					if (directorio[0]!=0) {
									debug_printf (VERBOSE_INFO,"Changing to last directory: %s",directorio);
									zvfs_chdir(directorio);
					}
	}


	int ret;

	ret=menu_filesel("Select AY File",filtros,last_ay_file);
	//volvemos a directorio inicial
	zvfs_chdir(directorio_actual);


	if (ret==1) {


		/*ay_player_load_and_play(last_ay_file);

        //Inicializar la playlist y dejar solo este
        //TODO: eliminar todos los elementos que pudiera haber en la playlist, liberando memoria
        ay_player_playlist_init();
        ay_player_playlist_item_actual=0;
        ay_player_playlist_add(last_ay_file);*/

        ay_player_add_file(last_ay_file);

	}
}

/*
void menu_ay_player_add_item_playlist(MENU_ITEM_PARAMETERS)
{
	char *filtros[2];

	filtros[0]="ay";

	filtros[1]=0;

	//guardamos directorio actual
	char directorio_actual[PATH_MAX];
	getcwd(directorio_actual,PATH_MAX);

	//Obtenemos directorio de ultimo archivo
	//si no hay directorio, vamos a rutas predefinidas
	if (last_ay_file[0]==0) menu_chdir_sharedfiles();

	else {
					char directorio[PATH_MAX];
					util_get_dir(last_ay_file,directorio);
					//printf ("strlen directorio: %d directorio: %s\n",strlen(directorio),directorio);

					//cambiamos a ese directorio, siempre que no sea nulo
					if (directorio[0]!=0) {
									debug_printf (VERBOSE_INFO,"Changing to last directory: %s",directorio);
									zvfs_chdir(directorio);
					}
	}


	int ret;

	ret=menu_filesel("Select AY File",filtros,last_ay_file);
	//volvemos a directorio inicial
	zvfs_chdir(directorio_actual);


	if (ret==1) {


		ay_player_playlist_add(last_ay_file);

	}
}

*/

/*
void menu_ay_player_exit_tracker(MENU_ITEM_PARAMETERS)
{
	ay_player_stop_player();
}
*/

//Retorna indice a string incrementando en 1
int menu_ay_player_get_continuous_string(zxvision_window *w,int indice_actual,char *string,int *retardo)
{
    int longitud_texto=strlen(string);

    //Si el contador actual se ha pasado de longitud, volver
    if (indice_actual>longitud_texto) return 0;


	if ( (*retardo)<10 ) {
		(*retardo)++;
		return 0;
	}

    int visible_width=w->visible_width;



    //Si cabe, no rotar. quitamos 2 de margenes izquierdo y derecho
    if (longitud_texto<=visible_width-2) return 0;


    if (string[indice_actual]!=0) {
        indice_actual++;
    }

    else {
        indice_actual=0;
        *retardo=0;
    }

	return indice_actual;
}





int menu_audio_new_ayplayer_si_mostrar(void)
{
	int mostrar_player;


	mostrar_player=1;
	if (audio_ay_player_mem==NULL) mostrar_player=0;
	if (ay_player_playing.v==0) mostrar_player=0;

	return mostrar_player;
}


//Usado dentro del overlay de ayplayer
int menu_ayplayer_valor_contador_segundo_anterior;

int ayplayer_new_contador_string_author=0;
int ayplayer_new_contador_string_track_name=0;
int ayplayer_new_contador_string_misc=0;
int ayplayer_new_retardo_song_name=0;
int ayplayer_new_retardo_author=0;
int ayplayer_new_retardo_misc=0;


//Para hacer las barras de volumen con "caracter" que decae
int ayplayer_previo_valor_escalado=0;

int ayplayer_previo_valor_volume_A=0;
int ayplayer_previo_valor_volume_B=0;
int ayplayer_previo_valor_volume_C=0;

zxvision_window *menu_audio_new_ayplayer_overlay_window;

#define AYPLAYER_ALTO_VENTANA 25
#define AYPLAYER_INICIO_LINEA_TRACK 3
#define AYPLAYER_INICIO_LINEA_VU_METERS 10
#define AYPLAYER_INICIO_LINEA_MENU 15


int ayplayer_force_refresh=0;

void menu_audio_new_ayplayer_overlay(void)
{



    menu_speech_set_tecla_pulsada(); //Si no, envia continuamente todo ese texto a speech

    //si ventana minimizada, no ejecutar todo el codigo de overlay
    if (menu_audio_new_ayplayer_overlay_window->is_minimized) return;

    //printf("overlay ay player %d\n",contador_segundo);

    int linea;



	int valor_escalado;

	int vol_A,vol_B,vol_C;



    //Los volumenes mostrarlos siempre a cada refresco
    if (menu_audio_new_ayplayer_si_mostrar()) {
        //Linea Track. Refrescar siempre para que se muevan las decimas de segundo con cada frame
        z80_byte minutos,segundos,decimas_segundo,minutos_total,segundos_total,decimas_segundo_total;
        ay_player_get_elapsed_current_song(&minutos,&segundos,&decimas_segundo);


        ay_player_get_duration_current_song(&minutos_total,&segundos_total,&decimas_segundo_total);


        zxvision_print_string_defaults_fillspc_format(menu_audio_new_ayplayer_overlay_window,1,AYPLAYER_INICIO_LINEA_TRACK,
            "Track: %03d/%03d  (%02d:%02d.%02d/%02d:%02d.%02d)",ay_player_pista_actual,ay_player_total_songs(),
            minutos,segundos,decimas_segundo,
            minutos_total,segundos_total,decimas_segundo_total);

        char volumen[32];
        char textovolumen[35]; //32+3 de posible color rojo del maximo

        //repetido
        //menu_speech_set_tecla_pulsada(); //Si no, envia continuamente todo ese texto a speech

        vol_A=ay_3_8912_registros[0][8] & 15;
        vol_B=ay_3_8912_registros[0][9] & 15;
        vol_C=ay_3_8912_registros[0][10] & 15;

        ayplayer_previo_valor_volume_A=menu_decae_ajusta_valor_volumen(ayplayer_previo_valor_volume_A,vol_A);
        ayplayer_previo_valor_volume_B=menu_decae_ajusta_valor_volumen(ayplayer_previo_valor_volume_B,vol_B);
        ayplayer_previo_valor_volume_C=menu_decae_ajusta_valor_volumen(ayplayer_previo_valor_volume_C,vol_C);

        linea=AYPLAYER_INICIO_LINEA_VU_METERS;

	    menu_string_volumen(volumen,ay_3_8912_registros[0][8],ayplayer_previo_valor_volume_A);
        sprintf (textovolumen,"Volume A: %s",volumen);
        //menu_escribe_linea_opcion(linea++,-1,1,textovolumen);
        zxvision_print_string_defaults_fillspc(menu_audio_new_ayplayer_overlay_window,1,linea++,textovolumen);

        menu_string_volumen(volumen,ay_3_8912_registros[0][9],ayplayer_previo_valor_volume_B);
        sprintf (textovolumen,"Volume B: %s",volumen);
        //menu_escribe_linea_opcion(linea++,-1,1,textovolumen);
        zxvision_print_string_defaults_fillspc(menu_audio_new_ayplayer_overlay_window,1,linea++,textovolumen);

        menu_string_volumen(volumen,ay_3_8912_registros[0][10],ayplayer_previo_valor_volume_C);
        sprintf (textovolumen,"Volume C: %s",volumen);
        //menu_escribe_linea_opcion(linea++,-1,1,textovolumen);
        zxvision_print_string_defaults_fillspc(menu_audio_new_ayplayer_overlay_window,1,linea++,textovolumen);



        //Obtenemos antes valor medio total y tambien maximo y minimo
        //Esto solo es necesario para dibujar onda llena



        audiobuffer_stats audiostats;
        audio_get_audiobuffer_stats(&audiostats);

        //int volumen_buffer=audiostats.volumen;

        //Ahora tenemos valor entre 0 y 128. Pasar a entre 0 y 15
        //int valor_escalado=(mayor*16)/128;

        valor_escalado=audiostats.volumen_escalado;

        /*

        int valor_escalado=(volumen_buffer*16)/128;

        //Vigilar que no pase de 15
        if (valor_escalado>15) valor_escalado=15;
        */

        //printf ("audiomin: %d audiomax: %d maximo: %d valor_escalado: %d\n",audiomin,audiomax,mayor,valor_escalado);

        //Y mostramos indicador volumen
        /*
        Nota: realmente este calculo de volumen no es del todo cierto, estoy viendo el valor maximo de la onda, aunque se puede generar
        sonido muy bajo, por ejemplo, oscilando valores entre 100 y 120 (considerando signed 8 bits), es mas, hay juegos que, al usar beeper,
        "mueven" esa onda hacia arriba, y aunque el indicador de volumen diga que esta muy alto, realmente se oye a volumen normal
        Pero bueno, la mayoria de las veces si que coincide bien el valor de volumen
        */


        ayplayer_previo_valor_escalado=menu_decae_ajusta_valor_volumen(ayplayer_previo_valor_escalado,valor_escalado);
        //if (ayplayer_previo_valor_escalado<valor_escalado) ayplayer_previo_valor_escalado=valor_escalado;

        menu_string_volumen(volumen,valor_escalado,ayplayer_previo_valor_escalado);




                            //"Volume C: %s"
        sprintf (textovolumen,"Output:   %s",volumen);
        //menu_escribe_linea_opcion(linea++,-1,1,textovolumen);
        zxvision_print_string_defaults_fillspc(menu_audio_new_ayplayer_overlay_window,1,linea++,textovolumen);

	}

	else {
		//Borrar lineas
		int i;
		linea=0;
		for (i=0;i<AYPLAYER_INICIO_LINEA_MENU;i++) zxvision_print_string_defaults_fillspc(menu_audio_new_ayplayer_overlay_window,1,linea++,"");

        zxvision_print_string_defaults_fillspc_format(menu_audio_new_ayplayer_overlay_window,1,0,"No song playing");

	}

    //esto hara ejecutar esto 2 veces por segundo
    if ( ((contador_segundo%500) == 0 && menu_ayplayer_valor_contador_segundo_anterior!=contador_segundo) || menu_multitarea==0 || ayplayer_force_refresh) {

        menu_ayplayer_valor_contador_segundo_anterior=contador_segundo;
        //printf ("Refrescando. contador_segundo=%d\n",contador_segundo);

        //if (ayplayer_force_refresh) printf("forzado refresco\n");

        ayplayer_force_refresh=0;




        int mostrar_player;

	    mostrar_player=menu_audio_new_ayplayer_si_mostrar();


		if (mostrar_player) {

			linea=0;

            char nombre_archivo[PATH_MAX];

            util_get_file_no_directory(ay_player_filename_playing,nombre_archivo);

            zxvision_print_string_defaults_fillspc_format(menu_audio_new_ayplayer_overlay_window,1,linea++,"File: %s",nombre_archivo);


            zxvision_print_string_defaults_fillspc_format(menu_audio_new_ayplayer_overlay_window,1,linea++,
                "Player Version: %d Size: %d bytes",ay_player_version(),ay_player_size() );

            zxvision_print_string_defaults_fillspc_format(menu_audio_new_ayplayer_overlay_window,1,linea++,
                "Playlist: %d/%d",ay_player_playlist_item_actual+1,ay_player_playlist_get_total_elements() );

            //Indicadores de volumen que decaen
            ayplayer_previo_valor_escalado=menu_decae_dec_valor_volumen(ayplayer_previo_valor_escalado,valor_escalado);


            ayplayer_previo_valor_volume_A=menu_decae_dec_valor_volumen(ayplayer_previo_valor_volume_A,vol_A);
            ayplayer_previo_valor_volume_B=menu_decae_dec_valor_volumen(ayplayer_previo_valor_volume_B,vol_B);
            ayplayer_previo_valor_volume_C=menu_decae_dec_valor_volumen(ayplayer_previo_valor_volume_C,vol_C);


            //Linea Track
            //Vacia. Se refresca en todos los frames, mas arriba


            linea++;


			ayplayer_new_contador_string_track_name=menu_ay_player_get_continuous_string(menu_audio_new_ayplayer_overlay_window,
                ayplayer_new_contador_string_track_name,ay_player_file_song_name,&ayplayer_new_retardo_song_name);
			zxvision_print_string_defaults_fillspc(menu_audio_new_ayplayer_overlay_window,1,linea++,&ay_player_file_song_name[ayplayer_new_contador_string_track_name]);


			zxvision_print_string_defaults_fillspc(menu_audio_new_ayplayer_overlay_window,1,linea++,"Author:");

			ayplayer_new_contador_string_author=menu_ay_player_get_continuous_string(menu_audio_new_ayplayer_overlay_window,
                ayplayer_new_contador_string_author,ay_player_file_author,&ayplayer_new_retardo_author);
			zxvision_print_string_defaults_fillspc(menu_audio_new_ayplayer_overlay_window,1,linea++,&ay_player_file_author[ayplayer_new_contador_string_author]);


			zxvision_print_string_defaults_fillspc(menu_audio_new_ayplayer_overlay_window,1,linea++,"Misc:");
			ayplayer_new_contador_string_misc=menu_ay_player_get_continuous_string(menu_audio_new_ayplayer_overlay_window,
                ayplayer_new_contador_string_misc,ay_player_file_misc,&ayplayer_new_retardo_misc);
			zxvision_print_string_defaults_fillspc(menu_audio_new_ayplayer_overlay_window,1,linea++,&ay_player_file_misc[ayplayer_new_contador_string_misc]);


		}
	}

	zxvision_draw_window_contents(menu_audio_new_ayplayer_overlay_window);
}



void menu_audio_new_ayplayer_load(MENU_ITEM_PARAMETERS)
{


	menu_ay_player_load(0);



}

void menu_audio_new_ayplayer_prev(MENU_ITEM_PARAMETERS)
{
	ay_player_previous_track();
    //ayplayer_force_refresh=1;

}

void menu_audio_new_ayplayer_next(MENU_ITEM_PARAMETERS)
{
	ay_player_next_track();
    //ayplayer_force_refresh=1;

}

void menu_audio_new_ayplayer_stop(MENU_ITEM_PARAMETERS)
{
	ay_player_stop_player();

}

void menu_audio_new_ayplayer_repeat(MENU_ITEM_PARAMETERS)
{
	ay_player_repeat_file.v ^=1;

}


void menu_audio_new_ayplayer_exitend(MENU_ITEM_PARAMETERS)
{
	ay_player_exit_emulator_when_finish.v ^=1;
}

void menu_audio_new_ayplayer_cpcmode(MENU_ITEM_PARAMETERS)
{
	ay_player_cpc_mode.v ^=1;
	audio_ay_player_play_song(ay_player_pista_actual);
}



void menu_audio_new_ayplayer_inftracks(MENU_ITEM_PARAMETERS)
{

	char string_length[5];
	sprintf(string_length,"%d",ay_player_limit_infinite_tracks/50);

        menu_ventana_scanf("Length (0-1310)",string_length,5);
	int l=parse_string_to_number(string_length);

	if (l<0 || l>1310) {
		menu_error_message("Invalid length value");
	}

	else ay_player_limit_infinite_tracks=l*50;


}

void menu_audio_new_ayplayer_add_to_track(MENU_ITEM_PARAMETERS)
{

	char string_length[3];
	sprintf(string_length,"%d",ay_player_add_to_track);

	menu_ventana_scanf("Length (0-60)",string_length,3);
	int l=parse_string_to_number(string_length);

	if (l<0 || l>60) {
		menu_error_message("Invalid length value");
	}

	else ay_player_add_to_track=l;


}

void menu_audio_new_ayplayer_len_anytracks(MENU_ITEM_PARAMETERS)
{



	char string_length[5];
	sprintf(string_length,"%d",ay_player_limit_any_track/50);

	menu_ventana_scanf("Length (0-1310)",string_length,5);
	int l=parse_string_to_number(string_length);

	if (l<0 || l>1310) {
		menu_error_message("Invalid length value");
	}

	else ay_player_limit_any_track=l*50;


}

void menu_audio_new_ayplayer_show_on_console(MENU_ITEM_PARAMETERS)
{
    ay_player_show_info_console.v ^=1;
}

void menu_ayplayer_shuffle(MENU_ITEM_PARAMETERS)
{
    ay_player_shuffle_mode.v ^=1;
}

void menu_ayplayer_detect_silence(MENU_ITEM_PARAMETERS)
{
    ay_player_silence_detection.v ^=1;
}

void menu_ayplayer_next_file(MENU_ITEM_PARAMETERS)
{
    ay_player_next_file();
    //ayplayer_force_refresh=1;
}

void menu_ayplayer_previous_file(MENU_ITEM_PARAMETERS)
{
    ay_player_previous_file();
    //ayplayer_force_refresh=1;
}

/*
void menu_ayplayer_start_playlist(MENU_ITEM_PARAMETERS)
{
    int total_elements=ay_player_playlist_get_total_elements();

    if (!total_elements) {
        debug_printf(VERBOSE_ERR,"Playlist is empty");
        return;
    }

    ay_player_playlist_item_actual=0;
    ay_player_play_current_item();
}

*/

void menu_ayplayer_edit_playlist_action(MENU_ITEM_PARAMETERS)
{

    int item_sonando=ay_player_playlist_item_actual;

    int item_seleccionado=valor_opcion;

    int opcion=menu_simple_three_choices("Playlist item","Do you want to:","Play","Mark/Unmark","Delete");

    switch(opcion) {
        case 1:
            ay_player_play_this_item(item_seleccionado);
        break;

        case 2:
            ay_player_mark_unmark_this_item(item_seleccionado);
            menu_generic_message_splash("Mark item","OK. Item marked/unmarked");
        break;

        case 3:
            ay_player_playlist_remove(item_seleccionado);
            //Si es el ultimo, detener
            if (ay_player_playlist_get_total_elements()==0) {
                ay_player_stop_player();
            }
            //Si el que estaba sonando es el que borramos, reproducir siguiente
            else if (item_sonando==item_seleccionado) {
                ay_player_play_this_item(item_seleccionado);
            }
            else {
                //Si el que borramos es anterior al actual, hay que decir que el actual es uno menos
                if (item_seleccionado<item_sonando) {
                    ay_player_playlist_item_actual--;
                }
            }
        break;
    }


}

void menu_ayplayer_add_directory_playlist(MENU_ITEM_PARAMETERS)
{
    char *filtros[2];

    filtros[0]="ay";
    filtros[1]=0;


    //guardamos directorio actual
    char directorio_actual[PATH_MAX];
    getcwd(directorio_actual,PATH_MAX);

    int ret;


    char nada[PATH_MAX];

    //Obtenemos ultimo directorio visitado
    //zvfs_chdir(string_root_dir);


    ret=menu_filesel("Enter dir & press ESC",filtros,nada);


    //Si sale con ESC
    if (ret==0) {
        //printf("Add dir %s\n",menu_filesel_last_directory_seen);
        ay_player_add_directory_playlist(menu_filesel_last_directory_seen);
    }

    //volvemos a directorio inicial
    zvfs_chdir(directorio_actual);



}

void menu_ayplayer_start_playing_playlist(MENU_ITEM_PARAMETERS)
{
    ay_player_play_this_item(0);
}

void menu_ayplayer_append_playlist(MENU_ITEM_PARAMETERS)
{


	char archivo_playlist[PATH_MAX];

	char *filtros[2];



    filtros[0]="m3u";
    filtros[1]=0;



	if (menu_filesel("Select Playlist File",filtros,archivo_playlist)==1) {

        ay_player_load_playlist(archivo_playlist);

    }
}

void menu_ayplayer_playlist_remove_all(MENU_ITEM_PARAMETERS)
{


    if (menu_confirm_yesno("Clear playlist")) {
        ay_player_playlist_remove_all();
        ay_player_stop_player();
        menu_generic_message_splash("Clear playlist","OK. Playlist has been cleared");
    }

}

void menu_ayplayer_load_playlist(MENU_ITEM_PARAMETERS)
{

	ay_player_playlist_remove_all();

    menu_ayplayer_append_playlist(0);
}

void menu_ayplayer_save_playlist_common(int marked_items)
{

	char destination_file[PATH_MAX];

	char *filtros[2];



    filtros[0]="m3u";
    filtros[1]=0;



	if (menu_filesel_save("Select Playlist File",filtros,destination_file)==1) {

		//Ver si archivo existe y preguntar
        int append=0;

		if (si_existe_archivo(destination_file)) {

            int opcion=menu_ask_no_append_truncate_texto("File exists","What do you want?",0);

            switch (opcion) {
                //Cancel
                case 0:
                    return;
                break;

                //Append
                case 1:
                    append=1;
                break;

                //Truncate
                case 2:
                    util_truncate_file(destination_file);
                break;

            }


		}

        ay_player_save_playlist(destination_file,marked_items,append);


        //Si ha ido bien la grabacion
        if (!if_pending_error_message) menu_generic_message_splash("Save Playlist","OK. Playlist saved");

    }

}

void menu_ayplayer_save_playlist(MENU_ITEM_PARAMETERS)
{
    menu_ayplayer_save_playlist_common(0);
}

void menu_ayplayer_save_playlist_marked_items(MENU_ITEM_PARAMETERS)
{
    menu_ayplayer_save_playlist_common(1);
}


void menu_ayplayer_mark_file(MENU_ITEM_PARAMETERS)
{
    ay_player_mark_unmark_this_item(ay_player_playlist_item_actual);

    menu_generic_message_splash("Mark item","OK. Item marked/unmarked");
}

void menu_ayplayer_edit_playlist(MENU_ITEM_PARAMETERS)
{
    menu_item *array_menu_common;
    menu_item item_seleccionado;
    int retorno_menu;





    do {

        //Linea seleccionada sera el archivo reproduciendose
        //Saltando las lineas de opciones anteriores
        int linea_seleccionada=0;

        menu_add_item_menu_inicial(&array_menu_common,"",MENU_OPCION_UNASSIGNED,NULL,NULL);

        if (ay_player_playlist_get_total_elements()==0) {
            menu_add_item_menu_format(array_menu_common,MENU_OPCION_NORMAL,NULL,NULL,"<Empty>");
            linea_seleccionada++;
        }

        menu_add_item_menu_format(array_menu_common,MENU_OPCION_NORMAL,menu_ayplayer_add_directory_playlist,NULL,"Add directory");
        linea_seleccionada++;
        menu_add_item_menu_add_flags(array_menu_common,MENU_ITEM_FLAG_GENERA_VENTANA);


        menu_add_item_menu_format(array_menu_common,MENU_OPCION_NORMAL,menu_ayplayer_load_playlist,NULL,"Load playlist");
        linea_seleccionada++;
        //menu_add_item_menu_add_flags(array_menu_common,MENU_ITEM_FLAG_GENERA_VENTANA | MENU_ITEM_FLAG_SE_CERRARA);
        menu_add_item_menu_add_flags(array_menu_common,MENU_ITEM_FLAG_GENERA_VENTANA);

        if (ay_player_playlist_get_total_elements()!=0) {

            menu_add_item_menu_format(array_menu_common,MENU_OPCION_NORMAL,menu_ayplayer_append_playlist,NULL,"Append playlist");
            linea_seleccionada++;
            menu_add_item_menu_add_flags(array_menu_common,MENU_ITEM_FLAG_GENERA_VENTANA);

            menu_add_item_menu_format(array_menu_common,MENU_OPCION_NORMAL,menu_ayplayer_save_playlist,NULL,"Save playlist");
            linea_seleccionada++;
            menu_add_item_menu_add_flags(array_menu_common,MENU_ITEM_FLAG_GENERA_VENTANA);

            menu_add_item_menu_format(array_menu_common,MENU_OPCION_NORMAL,menu_ayplayer_save_playlist_marked_items,NULL,"Save marked items playlist");
            linea_seleccionada++;
            menu_add_item_menu_add_flags(array_menu_common,MENU_ITEM_FLAG_GENERA_VENTANA);

            menu_add_item_menu_format(array_menu_common,MENU_OPCION_NORMAL,menu_ayplayer_playlist_remove_all,NULL,"Clear playlist");
            linea_seleccionada++;
            menu_add_item_menu_add_flags(array_menu_common,MENU_ITEM_FLAG_GENERA_VENTANA);


            menu_add_item_menu_separator(array_menu_common);
            linea_seleccionada++;

            menu_add_item_menu_format(array_menu_common,MENU_OPCION_NORMAL,NULL,NULL,"Playlist contents:");
            linea_seleccionada++;

            linea_seleccionada+=ay_player_playlist_item_actual;

            //Recorrer toda la playlist
            ay_player_playlist_item *playitem=ay_player_first_item_playlist;

            int i=0;
            while (playitem!=NULL) {
                char nombre_archivo[PATH_MAX];

                util_get_file_no_directory(playitem->nombre,nombre_archivo);

                menu_add_item_menu_format(array_menu_common,MENU_OPCION_NORMAL,menu_ayplayer_edit_playlist_action,NULL,
                    "%c%s",(playitem->marcado ? '*' : ' ' ),nombre_archivo);
                menu_add_item_menu_valor_opcion(array_menu_common,i);

                playitem=playitem->next_item;
                i++;
            }

        }



        menu_add_item_menu_separator(array_menu_common);

        menu_add_ESC_item(array_menu_common);

        retorno_menu=menu_dibuja_menu_dialogo_no_title_lang(&linea_seleccionada,&item_seleccionado,array_menu_common,"Playlist");

        //no queremos que al pulsar ESC aqui se cierren todos los menus anteriores
        salir_todos_menus=0;

        if ((item_seleccionado.tipo_opcion&MENU_OPCION_ESC)==0 && retorno_menu>=0) {
            //llamamos por valor de funcion
            if (item_seleccionado.menu_funcion!=NULL) {
                //printf ("actuamos por funcion\n");
                item_seleccionado.menu_funcion(item_seleccionado.valor_opcion);

            }
        }

    } while ( (item_seleccionado.tipo_opcion&MENU_OPCION_ESC)==0 && retorno_menu!=MENU_RETORNO_ESC && !salir_todos_menus);
}

zxvision_window zxvision_window_ayplayer;

//Cambiar el titulo segun si esta en modo pause o no
void ayplayer_set_window_title(void)
{
    if (ay_player_paused.v==0) {
        strcpy(zxvision_window_ayplayer.window_title,"AY Player");
    }
    else {
        strcpy(zxvision_window_ayplayer.window_title,"AY Player (Paused)");
    }
}

void menu_audio_new_ayplayer_pause(MENU_ITEM_PARAMETERS)
{
    ay_player_pause_unpause();
}




void menu_audio_new_ayplayer(MENU_ITEM_PARAMETERS)
{


    if (!menu_multitarea) {
        menu_warn_message("This window needs multitask enabled");
        return;
    }

 	menu_espera_no_tecla();
	menu_reset_counters_tecla_repeticion();

	//zxvision_window ventana;
    zxvision_window *ventana;
    ventana=&zxvision_window_ayplayer;

    //IMPORTANTE! no crear ventana si ya existe. Esto hay que hacerlo en todas las ventanas que permiten background.
    //si no se hiciera, se crearia la misma ventana, y en la lista de ventanas activas , al redibujarse,
    //la primera ventana repetida apuntaria a la segunda, que es el mismo puntero, y redibujaria la misma, y se quedaria en bucle colgado
    //zxvision_delete_window_if_exists(ventana);

    //Crear ventana si no existe
    if (!zxvision_if_window_already_exists(ventana)) {

        int xventana,yventana,ancho_ventana,alto_ventana,is_minimized,is_maximized,ancho_antes_minimize,alto_antes_minimize;


        if (!util_find_window_geometry("ayplayer",&xventana,&yventana,&ancho_ventana,&alto_ventana,&is_minimized,&is_maximized,&ancho_antes_minimize,&alto_antes_minimize)) {
            ancho_ventana=37;
            alto_ventana=AYPLAYER_ALTO_VENTANA;

            xventana=menu_center_x()-ancho_ventana/2;
            yventana=menu_center_y()-alto_ventana/2;
        }





        zxvision_new_window_gn_cim(ventana,xventana,yventana,ancho_ventana,alto_ventana,ancho_ventana-1,alto_ventana-2,"AY Player","ayplayer",
            is_minimized,is_maximized,ancho_antes_minimize,alto_antes_minimize);

        ventana->can_be_backgrounded=1;
        //indicar nombre del grabado de geometria
        //strcpy(ventana->geometry_name,"ayplayer");
        //restaurar estado minimizado de ventana
        //ventana->is_minimized=is_minimized;

        //no indicar que se ha escrito mas alla del tamanyo, porque los textos de titulo etc pueden ser muy largos (y ya se hara scroll automatico)
        ventana->do_not_warn_tried_write_beyond_size=1;

    }

    //Si ya existe, activar esta ventana
    else {

        zxvision_activate_this_window(ventana);
    }

	zxvision_draw_window(ventana);

    menu_audio_new_ayplayer_overlay_window=ventana; //Decimos que el overlay lo hace sobre la ventana que tenemos aqui

    //Cambiamos funcion overlay de texto de menu
    //Se establece a la de funcion de audio ay player

    //cambio overlay
    zxvision_set_window_overlay(ventana,menu_audio_new_ayplayer_overlay);




    //Toda ventana que este listada en zxvision_known_window_names_array debe permitir poder salir desde aqui
    //Se sale despues de haber inicializado overlay y de cualquier otra variable que necesite el overlay
    if (zxvision_currently_restoring_windows_on_start) {
            //printf ("Saliendo de ventana ya que la estamos restaurando en startup\n");
            return;
    }



	menu_item *array_menu_audio_new_ayplayer;
        menu_item item_seleccionado;
        int retorno_menu;
        do {

            ayplayer_set_window_title();

            ayplayer_new_contador_string_author=0;
            ayplayer_new_contador_string_track_name=0;
            ayplayer_new_contador_string_misc=0;

            ayplayer_new_retardo_song_name=0;
            ayplayer_new_retardo_author=0;
            ayplayer_new_retardo_misc=0;

        	char textoplayer[40];
            //Hay que redibujar la ventana desde este bucle
            //menu_audio_new_ayplayer_dibuja_ventana();

			int inicio_linea_menu=AYPLAYER_INICIO_LINEA_MENU;
            //Vamos a borrar con espacios para que no quede rastro de opciones anteriores, como Yes/No
            //Si no, pasaria que mostraria "Nos" como parte de la s final de Yes
            int i;
            for (i=inicio_linea_menu;i<=AYPLAYER_ALTO_VENTANA;i++) {
                zxvision_fill_width_spaces(ventana,i);
            }

            int linea=inicio_linea_menu;

            zxvision_print_string_defaults(ventana,1,linea,"Playlist: ");

            int marcado=0;

            menu_add_item_menu_inicial(&array_menu_audio_new_ayplayer,"",MENU_OPCION_UNASSIGNED,NULL,NULL);

            menu_add_item_menu_format(array_menu_audio_new_ayplayer,MENU_OPCION_NORMAL,menu_audio_new_ayplayer_load,NULL,"~~Add");
            menu_add_item_menu_shortcut(array_menu_audio_new_ayplayer,'a');
            menu_add_item_menu_ayuda(array_menu_audio_new_ayplayer,"Add AY file");
            menu_add_item_menu_tabulado(array_menu_audio_new_ayplayer,11,linea);

            if (menu_audio_new_ayplayer_si_mostrar() ) {

                menu_add_item_menu_format(array_menu_audio_new_ayplayer,MENU_OPCION_NORMAL,menu_ayplayer_previous_file,NULL,"P~~rev");
                menu_add_item_menu_shortcut(array_menu_audio_new_ayplayer,'r');
                menu_add_item_menu_ayuda(array_menu_audio_new_ayplayer,"Play previous file");
                menu_add_item_menu_tabulado(array_menu_audio_new_ayplayer,15,linea);

                menu_add_item_menu_format(array_menu_audio_new_ayplayer,MENU_OPCION_NORMAL,menu_ayplayer_next_file,NULL,"N~~ext");
                menu_add_item_menu_shortcut(array_menu_audio_new_ayplayer,'e');
                menu_add_item_menu_ayuda(array_menu_audio_new_ayplayer,"Play next file");
                menu_add_item_menu_tabulado(array_menu_audio_new_ayplayer,20,linea);




                ay_player_playlist_item *current_item=ay_player_search_item(ay_player_playlist_item_actual);
                if (current_item!=NULL) {
                    marcado=current_item->marcado;
                }

                menu_add_item_menu_format(array_menu_audio_new_ayplayer,MENU_OPCION_NORMAL,menu_ayplayer_mark_file,NULL,"%s",
                    (marcado ? "Un~~mark" : "~~Mark") );
                menu_add_item_menu_shortcut(array_menu_audio_new_ayplayer,'m');
                menu_add_item_menu_ayuda(array_menu_audio_new_ayplayer,"Mark/unmark file");
                menu_add_item_menu_tabulado(array_menu_audio_new_ayplayer,25,linea);

            }

            /*
            menu_add_item_menu_format(array_menu_audio_new_ayplayer,MENU_OPCION_NORMAL,menu_ayplayer_start_playlist,NULL,"Begin");
            //menu_add_item_menu_shortcut(array_menu_audio_new_ayplayer,'a');
            //menu_add_item_menu_ayuda(array_menu_audio_new_ayplayer,"Add AY file");
            menu_add_item_menu_tabulado(array_menu_audio_new_ayplayer,25,linea);
            */

            menu_add_item_menu_format(array_menu_audio_new_ayplayer,MENU_OPCION_NORMAL,menu_ayplayer_edit_playlist,NULL,"E~~dit");
            menu_add_item_menu_shortcut(array_menu_audio_new_ayplayer,'d');
            menu_add_item_menu_ayuda(array_menu_audio_new_ayplayer,"Edit playlist");
            menu_add_item_menu_tabulado(array_menu_audio_new_ayplayer,32,linea);

            /*menu_add_item_menu_format(array_menu_audio_new_ayplayer,MENU_OPCION_NORMAL,NULL,NULL,
                "playlist (%d/%d)",ay_player_playlist_item_actual+1,ay_player_playlist_get_total_elements() );
            //menu_add_item_menu_shortcut(array_menu_audio_new_ayplayer,'l');
            //menu_add_item_menu_ayuda(array_menu_audio_new_ayplayer,"Load AY file");
            menu_add_item_menu_tabulado(array_menu_audio_new_ayplayer,15,inicio_linea_menu);    */

            linea++;

			if (menu_audio_new_ayplayer_si_mostrar() ) {

                zxvision_print_string_defaults(ventana,1,linea,"Track: ");

				menu_add_item_menu_format(array_menu_audio_new_ayplayer,MENU_OPCION_NORMAL,menu_audio_new_ayplayer_prev,NULL,"~~Prev");
				menu_add_item_menu_shortcut(array_menu_audio_new_ayplayer,'p');
				menu_add_item_menu_ayuda(array_menu_audio_new_ayplayer,"Previous song");
				menu_add_item_menu_tabulado(array_menu_audio_new_ayplayer,11,linea);

				menu_add_item_menu_format(array_menu_audio_new_ayplayer,MENU_OPCION_NORMAL,menu_audio_new_ayplayer_stop,NULL,"~~Stop");
				menu_add_item_menu_shortcut(array_menu_audio_new_ayplayer,'s');
				menu_add_item_menu_ayuda(array_menu_audio_new_ayplayer,"Stop song");
				menu_add_item_menu_tabulado(array_menu_audio_new_ayplayer,16,linea);


				menu_add_item_menu_format(array_menu_audio_new_ayplayer,MENU_OPCION_NORMAL,menu_audio_new_ayplayer_pause,NULL,"Pa~~use");
				menu_add_item_menu_shortcut(array_menu_audio_new_ayplayer,'u');
				menu_add_item_menu_ayuda(array_menu_audio_new_ayplayer,"Pause song");
				menu_add_item_menu_tabulado(array_menu_audio_new_ayplayer,21,linea);

				menu_add_item_menu_format(array_menu_audio_new_ayplayer,MENU_OPCION_NORMAL,menu_audio_new_ayplayer_next,NULL,"~~Next");
				menu_add_item_menu_shortcut(array_menu_audio_new_ayplayer,'n');
				menu_add_item_menu_ayuda(array_menu_audio_new_ayplayer,"Next song");
				menu_add_item_menu_tabulado(array_menu_audio_new_ayplayer,27,linea);

                linea+=2;


                menu_add_item_menu_format(array_menu_audio_new_ayplayer,MENU_OPCION_NORMAL,menu_ayplayer_shuffle,NULL,"[%c] Shuffle",
                    (ay_player_shuffle_mode.v ? 'X' : ' '));
                menu_add_item_menu_ayuda(array_menu_audio_new_ayplayer,"Random playlist playing");
                menu_add_item_menu_tabulado(array_menu_audio_new_ayplayer,1,linea);


				menu_add_item_menu_format(array_menu_audio_new_ayplayer,MENU_OPCION_NORMAL,menu_audio_new_ayplayer_repeat,NULL,"[%c] Repeat",
					(ay_player_repeat_file.v ? 'X' : ' '));
				menu_add_item_menu_ayuda(array_menu_audio_new_ayplayer,"Repeat file from the beginning when finished all tracks of a file");
				menu_add_item_menu_tabulado(array_menu_audio_new_ayplayer,14,linea);

				menu_add_item_menu_format(array_menu_audio_new_ayplayer,MENU_OPCION_NORMAL,menu_audio_new_ayplayer_exitend,NULL,"[%c] Exitend",
					(ay_player_exit_emulator_when_finish.v ? 'X' : ' ') );
				menu_add_item_menu_ayuda(array_menu_audio_new_ayplayer,"Exit emulator when finished all files");
				menu_add_item_menu_tabulado(array_menu_audio_new_ayplayer,25,linea);

                linea++;

				menu_add_item_menu_format(array_menu_audio_new_ayplayer,MENU_OPCION_NORMAL,menu_audio_new_ayplayer_cpcmode,NULL,"[%c] CPC mode",
					(ay_player_cpc_mode.v ? 'X' : ' '));
				menu_add_item_menu_ayuda(array_menu_audio_new_ayplayer,"Switch to AY CPC mode");
				menu_add_item_menu_tabulado(array_menu_audio_new_ayplayer,1,linea);

				menu_add_item_menu_format(array_menu_audio_new_ayplayer,MENU_OPCION_NORMAL,menu_audio_new_ayplayer_show_on_console,NULL,"[%c] Console print",
					(ay_player_show_info_console.v ? 'X' : ' '));
				menu_add_item_menu_ayuda(array_menu_audio_new_ayplayer,"Show information about AY file and song played in console too");
				menu_add_item_menu_tabulado(array_menu_audio_new_ayplayer,14,linea);

                linea++;

                zxvision_print_string_defaults_fillspc_format(ventana,1,linea++,"--Track settings--");


				if (ay_player_limit_infinite_tracks==0) sprintf(textoplayer,"[no] Inf. Len");
				else sprintf(textoplayer,"[%ds] Inf. Len",ay_player_limit_infinite_tracks/50);
				menu_add_item_menu_format(array_menu_audio_new_ayplayer,MENU_OPCION_NORMAL,menu_audio_new_ayplayer_inftracks,NULL,textoplayer);
				menu_add_item_menu_ayuda(array_menu_audio_new_ayplayer,"Time limit for songs which doesn't have time limit (they are \"infinite\")");
				menu_add_item_menu_tabulado(array_menu_audio_new_ayplayer,1,linea);

				if (ay_player_limit_any_track==0) sprintf(textoplayer,"[no limit] Max len");
				else sprintf(textoplayer,"[%ds] Max len",ay_player_limit_any_track/50);
				menu_add_item_menu_format(array_menu_audio_new_ayplayer,MENU_OPCION_NORMAL,menu_audio_new_ayplayer_len_anytracks,NULL,textoplayer);
				menu_add_item_menu_ayuda(array_menu_audio_new_ayplayer,"Time limit for all songs");
				menu_add_item_menu_tabulado(array_menu_audio_new_ayplayer,18,linea);

                linea++;

				menu_add_item_menu_format(array_menu_audio_new_ayplayer,MENU_OPCION_NORMAL,menu_audio_new_ayplayer_add_to_track,NULL,
                    "[%ds] Add Len",ay_player_add_to_track);
				menu_add_item_menu_ayuda(array_menu_audio_new_ayplayer,"Increase track duration");
				menu_add_item_menu_tabulado(array_menu_audio_new_ayplayer,1,linea);




				menu_add_item_menu_format(array_menu_audio_new_ayplayer,MENU_OPCION_NORMAL,menu_ayplayer_detect_silence,NULL,"[%c] Detect silence",
					(ay_player_silence_detection.v ? 'X' : ' '));
				menu_add_item_menu_ayuda(array_menu_audio_new_ayplayer,"Jump to next track if silence detected during 10 seconds");
				menu_add_item_menu_tabulado(array_menu_audio_new_ayplayer,18,linea);


			}

            else if (ay_player_playlist_get_total_elements()!=0) {

				menu_add_item_menu_format(array_menu_audio_new_ayplayer,MENU_OPCION_NORMAL,menu_ayplayer_start_playing_playlist,NULL,"Start playing playlist");
				menu_add_item_menu_tabulado(array_menu_audio_new_ayplayer,11,linea);

            }




		//Nombre de ventana solo aparece en el caso de stdout
        retorno_menu=menu_dibuja_menu_no_title_lang(&audio_new_ayplayer_opcion_seleccionada,&item_seleccionado,array_menu_audio_new_ayplayer,"AY Player" );

	if (retorno_menu!=MENU_RETORNO_BACKGROUND) {

	//En caso de menus tabulados, es responsabilidad de este de borrar la ventana

                if ((item_seleccionado.tipo_opcion&MENU_OPCION_ESC)==0 && retorno_menu>=0) {
                        //llamamos por valor de funcion
                        if (item_seleccionado.menu_funcion!=NULL) {
                                //printf ("actuamos por funcion\n");
                                item_seleccionado.menu_funcion(item_seleccionado.valor_opcion);


                        }
                }
			}

        } while ( (item_seleccionado.tipo_opcion&MENU_OPCION_ESC)==0 && retorno_menu!=MENU_RETORNO_ESC && !salir_todos_menus && retorno_menu!=MENU_RETORNO_BACKGROUND);


    //En caso de menus tabulados, suele ser necesario esto. Si no, la ventana se quedaria visible
	util_add_window_geometry_compact(ventana);


    if (retorno_menu==MENU_RETORNO_BACKGROUND) {
            zxvision_message_put_window_background();
    }

    else {

            //En caso de menus tabulados, es responsabilidad de este de liberar ventana
            zxvision_destroy_window(ventana);
    }



}





//#define DEBUG_HEXDUMP_WINDOW_X (menu_origin_x() )
//#define DEBUG_HEXDUMP_WINDOW_Y 1
#define DEBUG_HEXDUMP_WINDOW_ANCHO 32
#define DEBUG_HEXDUMP_WINDOW_ALTO 23



void menu_debug_hexdump_with_ascii(char *dumpmemoria,menu_z80_moto_int dir_leida,int bytes_por_linea,z80_byte valor_xor)
{
	//dir_leida=adjust_address_space_cpu(dir_leida);

	menu_debug_set_memory_zone_attr();


	int longitud_direccion=MAX_LENGTH_ADDRESS_MEMORY_ZONE;

	menu_debug_print_address_memory_zone(dumpmemoria,dir_leida);



	//cambiamos el 0 final por un espacio
	dumpmemoria[longitud_direccion]=' ';

	menu_debug_registers_dump_hex(&dumpmemoria[longitud_direccion+1],dir_leida,bytes_por_linea);

	//01234567890123456789012345678901
	//000FFF ABCDABCDABCDABCD 12345678

	//metemos espacio
	int offset=longitud_direccion+1+bytes_por_linea*2;

	dumpmemoria[offset]=' ';
	//dumpmemoria[offset]='X';

	//Tener en cuenta el valor xor

	menu_debug_registers_dump_ascii(&dumpmemoria[offset+1],dir_leida,bytes_por_linea,menu_debug_hexdump_with_ascii_modo_ascii,valor_xor);

	//printf ("%s\n",dumpmemoria);
}


menu_z80_moto_int menu_debug_hexdump_direccion=0;

int menu_hexdump_edit_position_x=0; //Posicion del cursor relativa al inicio del volcado hexa
int menu_hexdump_edit_position_y=0; //Posicion del cursor relativa al inicio del volcado hexa
int menu_hexdump_lineas_total=13;

//Donde esta el otro caracter que acompanya al nibble, en caso de cursor en zona hexa
int menu_hexdump_edit_position_x_nibble=1;

int menu_hexdump_edit_mode=0;
const int menu_hexdump_bytes_por_linea=8;

void menu_debug_hexdump_cursor_abajo(void);
void menu_debug_hexdump_cursor_arriba(void);

int menu_debug_hexdump_cursor_en_zona_ascii=0;

//Modo follow expresion: registros, valores, etc, como expresiones de breakpoint
int menu_hexdump_follow_mode=0;
//La expresion parseada como tokens
token_parser menu_hexdump_follow_expression[MAX_PARSER_TOKENS_NUM];

void menu_debug_hexdump_print_editcursor(zxvision_window *ventana,int x,int y,char caracter)
{


	//Inverso
	int papel=ESTILO_GUI_PAPEL_SELECCIONADO;
    int tinta=ESTILO_GUI_TINTA_SELECCIONADO;

	//Si multitarea esta off, no se vera el parpadeo. Entonces cambiar el caracter por cursor '_'
	if (!menu_multitarea) caracter='_';

	//putchar_menu_overlay_parpadeo(x,y,caracter,tinta,papel,1);
	zxvision_print_char_simple(ventana,x,y,tinta,papel,1,caracter);

}

void menu_debug_hexdump_print_editcursor_nibble(zxvision_window *ventana,int x,int y,char caracter)
{


	//Inverso
	int papel=ESTILO_GUI_PAPEL_SELECCIONADO;
    int tinta=ESTILO_GUI_TINTA_SELECCIONADO;

	//putchar_menu_overlay_parpadeo(x,y,caracter,tinta,papel,0);
	zxvision_print_char_simple(ventana,x,y,tinta,papel,0,caracter);

}

void menu_debug_hexdump_edit_cursor_izquierda(void)
{
	if (menu_hexdump_edit_position_x>0) {
		menu_hexdump_edit_position_x--;

		//Si en medio del espacio entre hexa y ascii
		if (menu_hexdump_edit_position_x==menu_hexdump_bytes_por_linea*2) menu_hexdump_edit_position_x--;
	}

	else {
		//Aparecer por la derecha
		menu_debug_hexdump_cursor_arriba();
		menu_hexdump_edit_position_x=menu_hexdump_bytes_por_linea*3;
	}

}

//escribiendo_memoria cursor indica que si estamos a la derecha de zona de edicion escribiendo,
//tiene que saltar a la zona izquierda de la zona ascii o hexa, al llegar a la derecha de dicha zona
void menu_debug_hexdump_edit_cursor_derecha(int escribiendo_memoria)
{

	//Hexdump. bytes_por_linea*2 espacio bytes_por_linea

	int ancho_linea=menu_hexdump_bytes_por_linea*3+1;

	if (menu_hexdump_edit_position_x<ancho_linea-1) {
		menu_hexdump_edit_position_x++;

		if (menu_hexdump_edit_position_x==menu_hexdump_bytes_por_linea*2) { //Fin zona derecha hexa
			if (escribiendo_memoria) {
				//Ponernos al inicio zona hexa de nuevo saltando siguiente linea
				menu_hexdump_edit_position_x=0;
				menu_debug_hexdump_cursor_abajo();
			}
			else {
				//Saltar a zona ascii
				menu_hexdump_edit_position_x++;
			}
		}
	}

	else {
		//Fin zona derecha ascii.
		menu_debug_hexdump_cursor_abajo();

		if (escribiendo_memoria) {
			//Ponernos en el principio zona ascii
			menu_hexdump_edit_position_x=menu_hexdump_bytes_por_linea*2+1;
		}
		else {
			menu_hexdump_edit_position_x=0;
		}
	}

}

void menu_debug_hexdump_cursor_arriba(void)
{
	int alterar_ptr=0;
    //arriba
    if (menu_hexdump_edit_mode) {
        if (menu_hexdump_edit_position_y>0) menu_hexdump_edit_position_y--;
        else alterar_ptr=1;
    }

    else {
        alterar_ptr=1;
    }

    if (alterar_ptr) {
        menu_debug_hexdump_direccion -=menu_hexdump_bytes_por_linea;
        menu_debug_hexdump_direccion=menu_debug_hexdump_adjusta_en_negativo(menu_debug_hexdump_direccion,menu_hexdump_bytes_por_linea);
    }
}

void menu_debug_hexdump_cursor_abajo(void)
{
	int alterar_ptr=0;
    //abajo
    if (menu_hexdump_edit_mode) {
        if (menu_hexdump_edit_position_y<menu_hexdump_lineas_total-1) menu_hexdump_edit_position_y++;
        else alterar_ptr=1;
    }
    else {
        alterar_ptr=1;
    }

    if (alterar_ptr) {
        menu_debug_hexdump_direccion +=menu_hexdump_bytes_por_linea;
    }
}

void menu_debug_hexdump_copy(void)
{


    char string_address[10];

    sprintf (string_address,"%XH",menu_debug_hexdump_direccion);
    menu_ventana_scanf("Source?",string_address,10);
	menu_z80_moto_int source=parse_string_to_number(string_address);

    sprintf (string_address,"%XH",source);
    menu_ventana_scanf("Destination?",string_address,10);
	menu_z80_moto_int destination=parse_string_to_number(string_address);

	int destzone=menu_change_memory_zone_list_title("Destination Zone");
	if (destzone==-2) return; //Pulsado ESC

	int origzone=menu_debug_memory_zone;


    strcpy (string_address,"1");
    menu_ventana_scanf("Length?",string_address,10);
	menu_z80_moto_int longitud=parse_string_to_number(string_address);



	if (menu_confirm_yesno("Copy bytes")) {
		for (;longitud>0;source++,destination++,longitud--) {
			menu_set_memzone(origzone);
			//Antes de escribir o leer, normalizar zona memoria
			menu_debug_set_memory_zone_attr();
			source=adjust_address_memory_size(source);
			z80_byte valor=menu_debug_get_mapped_byte(source);


			menu_set_memzone(destzone);
			//Antes de escribir o leer, normalizar zona memoria
			menu_debug_set_memory_zone_attr();
			destination=adjust_address_memory_size(destination);
			menu_debug_write_mapped_byte(destination,valor);
		}

		//dejar la zona origen tal cual
		menu_set_memzone(origzone);
	}


}

char menu_debug_hexdump_shift_bits_last_address[10]="";
char menu_debug_hexdump_shift_bits_last_direction[2]="l";
char menu_debug_hexdump_shift_bits_last_length[10]="1";

void menu_debug_hexdump_shift_bits(void)
{


    //char string_address[10];
    //char string_direction[2];

    if (menu_debug_hexdump_shift_bits_last_address[0]==0) {
        sprintf (menu_debug_hexdump_shift_bits_last_address,"%XH",menu_debug_hexdump_direccion);
    }

    if (menu_ventana_scanf("Start address?",menu_debug_hexdump_shift_bits_last_address,10)==-1) return;
	menu_z80_moto_int source=parse_string_to_number(menu_debug_hexdump_shift_bits_last_address);

    if (menu_ventana_scanf("Direction? (l/r)",menu_debug_hexdump_shift_bits_last_direction,2)==-1) return;

    if (menu_ventana_scanf("Length bytes?",menu_debug_hexdump_shift_bits_last_length,10)==-1) return;
	menu_z80_moto_int longitud=parse_string_to_number(menu_debug_hexdump_shift_bits_last_length);



	if (menu_confirm_yesno("Shift bits")) {
        int carry=0;


        if (menu_debug_hexdump_shift_bits_last_direction[0]=='r') {
            for (;longitud>0;source++,longitud--) {

                source=adjust_address_memory_size(source);
                z80_byte valor=menu_debug_get_mapped_byte(source);

                int newcarry=valor & 1;

                valor/=2;
                valor |=(carry ? 128 : 0);

                carry=newcarry;


                menu_debug_write_mapped_byte(source,valor);
            }

        }


        else if (menu_debug_hexdump_shift_bits_last_direction[0]=='l') {
            source +=longitud-1;
            for (;longitud>0;source--,longitud--) {

                source=adjust_address_memory_size(source);
                z80_byte valor=menu_debug_get_mapped_byte(source);

                int newcarry=valor & 128;

                valor *=2;
                valor |=(carry ? 1 : 0);

                carry=newcarry;

                menu_debug_write_mapped_byte(source,valor);
            }

        }

        else {
            menu_error_message("Invalid direction. It can only be left (l) or right (r)");
        }
	}


}

void menu_debug_hexdump_aviso_edit_filezone(zxvision_window *w)
{
    menu_warn_message("Memory zone is File zone. Changes won't be saved to the file");
    //Volver a dibujar ventana, pues se ha borrado al aparecer el aviso
    //menu_debug_hexdump_ventana();
	zxvision_draw_window(w);
}

void menu_debug_hexdump_info_subzones(void)
{

    int x=1;
    int y=1;
    int ancho=30;
    int alto=22;



    subzone_info *puntero;
    puntero=machine_get_memory_subzone_array(menu_debug_memory_zone,current_machine_type);
    if (puntero==NULL) return;

    zxvision_window ventana;

    zxvision_new_window(&ventana,x,y,ancho,alto,
                                            64,alto-2,"Memory subzones");

    zxvision_draw_window(&ventana);

    int i;

    char buffer_linea[64];
    for (i=0;puntero[i].nombre[0]!=0;i++) {

        //printf ("inicio: %d fin: %d texto: %s\n",puntero[i].inicio,puntero[i].fin,puntero[i].nombre);
        sprintf (buffer_linea,"%06X-%06X %s",puntero[i].inicio,puntero[i].fin,puntero[i].nombre);
        zxvision_print_string_defaults_fillspc(&ventana,1,i,buffer_linea);

    }

    zxvision_draw_window_contents(&ventana);

    zxvision_wait_until_esc(&ventana);



    zxvision_destroy_window(&ventana);


}

void menu_debug_hexdump_crea_ventana(zxvision_window *ventana,int x,int y,int ancho,int alto,
    int is_minimized,int is_maximized,int ancho_antes_minimize,int alto_antes_minimize)
{
	//asignamos mismo ancho visible que ancho total para poder usar la ultima columna de la derecha, donde se suele poner scroll vertical
    //en teoria ya no hace falta usar zxvision_new_window_nocheck_staticsize, desde hace tiempo que esto funciona correctamente
    //no da errores de tamaño al crear ventana
	//zxvision_new_window_nocheck_staticsize(ventana,x,y,ancho,alto,ancho,alto-2,"Hexadecimal Editor");

    //printf("hexdump crea ventana antes_minimize: %d X %d\n",ancho_antes_minimize,alto_antes_minimize);


    zxvision_new_window_gn_cim(ventana,x,y,ancho,alto,ancho,alto-2,"Hexadecimal Editor","hexeditor",is_minimized,is_maximized,ancho_antes_minimize,alto_antes_minimize);

    //printf("hexdump despues crea ventana antes_minimize: %d X %d\n",ventana->width_before_max_min_imize,ventana->height_before_max_min_imize);

	//printf ("ancho: %d alto: %d\n",ancho,alto);

	ventana->can_use_all_width=1; //Para poder usar la ultima columna de la derecha donde normalmente aparece linea scroll

	//indicar nombre del grabado de geometria
	//strcpy(ventana->geometry_name,"hexeditor");

    //restaurar estado minimizado de ventana
    //ventana->is_minimized=is_minimized;

	//Permitir hotkeys desde raton
	ventana->can_mouse_send_hotkeys=1;

    ventana->can_be_backgrounded=1;

	zxvision_draw_window(ventana);

}

menu_z80_moto_int menu_debug_hexdump_get_cursor_pointer(void)
{
    //Obtener direccion puntero
    menu_z80_moto_int direccion_cursor=menu_debug_hexdump_direccion;

    //int si_zona_hexa=0; //en zona hexa o ascii
    //if (menu_hexdump_edit_position_x<bytes_por_linea*2) si_zona_hexa=1;


    if (!menu_debug_hexdump_cursor_en_zona_ascii) {
        //Sumar x (cada dos, una posicion)
        direccion_cursor +=menu_hexdump_edit_position_x/2;
    }
    else {
        int indice_hasta_ascii=menu_hexdump_bytes_por_linea*2+1; //el hexa y el espacio
        direccion_cursor +=menu_hexdump_edit_position_x-indice_hasta_ascii;
    }

    //Sumar y.
    direccion_cursor +=menu_hexdump_edit_position_y*menu_hexdump_bytes_por_linea;

    //Ajustar direccion a zona memoria
    direccion_cursor=adjust_address_memory_size(direccion_cursor);


    return direccion_cursor;
}


z80_byte menu_hexdump_valor_xor=0;

char menu_hexdump_nibble_char='X';
char menu_hexdump_nibble_char_cursor='X';

int menu_hexdump_editando_en_zona_ascii=0;

int menu_hexdump_print_hexa_ascii(zxvision_window *ventana,int linea)
{


    int lineas_hex;
    char dumpmemoria[33];

    //Hacer que texto ventana empiece pegado a la izquierda
    menu_escribe_linea_startx=0;

    //No mostrar caracteres especiales en esta ventana, solo para la parte de dump hexa
    ventana->disable_special_chars=1;

    for (lineas_hex=0;lineas_hex<menu_hexdump_lineas_total;lineas_hex++,linea++) {

        menu_z80_moto_int dir_leida=menu_debug_hexdump_direccion+lineas_hex*menu_hexdump_bytes_por_linea;
        menu_debug_hexdump_direccion=adjust_address_memory_size(menu_debug_hexdump_direccion);

        menu_debug_hexdump_with_ascii(dumpmemoria,dir_leida,menu_hexdump_bytes_por_linea,menu_hexdump_valor_xor);

        zxvision_print_string_defaults_fillspc(ventana,0,linea,dumpmemoria);

        //Meter el nibble_char si corresponde
        if (lineas_hex==menu_hexdump_edit_position_y) {
            menu_hexdump_nibble_char_cursor=dumpmemoria[7+menu_hexdump_edit_position_x];
            if (!menu_hexdump_editando_en_zona_ascii) menu_hexdump_nibble_char=dumpmemoria[7+menu_hexdump_edit_position_x_nibble];
        }
    }

    menu_escribe_linea_startx=1;

    ventana->disable_special_chars=0;


    return linea;

}

zxvision_window *menu_debug_hexdump_overlay_window;

void menu_debug_hexdump_overlay(void)
{


    menu_speech_set_tecla_pulsada(); //Si no, envia continuamente todo ese texto a speech

    //si ventana minimizada, no ejecutar todo el codigo de overlay
    if (menu_debug_hexdump_overlay_window->is_minimized) return;

    //printf("Overlay hexdump %d\n",contador_segundo);

    zxvision_window *ventana;

    ventana=menu_debug_hexdump_overlay_window;

    //temp. seguir registro IX
    //menu_debug_hexdump_direccion=reg_ix-8*menu_hexdump_lineas_total;
    //En multiples de 8, que es lo que cabe en una linea asi se ira desplazando de linea en linea
    //menu_debug_hexdump_direccion &=(65535-7);

    //Asumimos siempre empieza en linea 2
    //No refrescar desde overlay si esta en edit mode



    //Si follow
    if (menu_hexdump_follow_mode) {

        int error_code;

        int resultado=exp_par_evaluate_token(menu_hexdump_follow_expression,MAX_PARSER_TOKENS_NUM,&error_code);

        if (error_code) {
            //printf("Error processing follow expression\n");
        }

        else {

            //printf("Resultado follow: %d\n",resultado);

            menu_debug_hexdump_direccion=resultado;

        }

    }

    if (!menu_hexdump_edit_mode) {
        menu_hexdump_print_hexa_ascii(ventana,2);
    }

    zxvision_draw_window_contents(ventana);

    //printf("Redibujando en background\n");

}
int menu_debux_hexdump_leyenda(zxvision_window *ventana,int linea)
{
    char textoshow[33];

    //Guardar estado atajos
    z80_bit antes_menu_writing_inverse_color;
    antes_menu_writing_inverse_color.v=menu_writing_inverse_color.v;

            //Forzar a mostrar atajos
        menu_writing_inverse_color.v=1;


//printf ("zone size: %x dir: %x\n",menu_debug_memory_zone_size,menu_debug_hexdump_direccion);

        //menu_escribe_linea_opcion(linea++,-1,1,"");
		zxvision_print_string_defaults_fillspc(ventana,1,linea++,"");

		char buffer_linea[64]; //Por si acaso, entre negritas y demas

		char buffer_char_type[20];

		char string_atajos[3]="~~";
		//Si esta en edit mode y en zona de ascii, no hay atajos



		if (menu_hexdump_editando_en_zona_ascii) string_atajos[0]=0;

		if (menu_debug_hexdump_with_ascii_modo_ascii==0) {
			sprintf (buffer_char_type,"ASCII");
		}

		else if (menu_debug_hexdump_with_ascii_modo_ascii==1) {
			sprintf (buffer_char_type,"ZX80");
		}

		else sprintf (buffer_char_type,"ZX81");



		//Si esta editando, mostrar puntero en leyenda de memptr
		char buffer_puntero[32];
		if (menu_hexdump_edit_mode) {
			menu_z80_moto_int direccion_cursor=menu_debug_hexdump_get_cursor_pointer();
			char buf_temp_pointer[32];

			menu_debug_print_address_memory_zone(buf_temp_pointer,direccion_cursor);
			sprintf(buffer_puntero," (%s)",buf_temp_pointer);
		}
		else {
			buffer_puntero[0]=0;
		}

		sprintf (buffer_linea,"%smemptr%s [%c] fo~~llow C%sopy ~~shiftbits",string_atajos,buffer_puntero,
            (menu_hexdump_follow_mode ? 'X' : ' '),
            string_atajos);


		//menu_escribe_linea_opcion(linea++,-1,1,buffer_linea);
		zxvision_print_string_defaults_fillspc(ventana,1,linea++,buffer_linea);

		sprintf (buffer_linea,"[%c] %sinvert [%c] Edi%st C%shar:%s",
			(menu_hexdump_valor_xor==0 ? ' ' : 'X'),
			string_atajos,

			(menu_hexdump_edit_mode==0 ? ' ' : 'X' ),
			string_atajos,

			string_atajos,
			buffer_char_type
			);
		//menu_escribe_linea_opcion(linea++,-1,1,buffer_linea);
		zxvision_print_string_defaults_fillspc(ventana,1,linea++,buffer_linea);


		char memory_zone_text[MACHINE_MAX_MEMORY_ZONE_NAME_LENGHT+100];
		if (menu_debug_show_memory_zones==0) {
			sprintf (memory_zone_text,"Mem %szone (mapped memory)",string_atajos);
		}
		else {
			//printf ("Info zona %d\n",menu_debug_memory_zone);
			char buffer_name[MACHINE_MAX_MEMORY_ZONE_NAME_LENGHT+1];
			//int readwrite;
			machine_get_memory_zone_name(menu_debug_memory_zone,buffer_name);
			sprintf (memory_zone_text,"Mem %szone (%d %s)",string_atajos,menu_debug_memory_zone,buffer_name);
			//printf ("size: %X\n",menu_debug_memory_zone_size);
			//printf ("Despues zona %d\n",menu_debug_memory_zone);
		}

		//menu_escribe_linea_opcion(linea++,-1,1,memory_zone_text);
		zxvision_print_string_defaults_fillspc(ventana,1,linea++,memory_zone_text);

		sprintf (textoshow," Size: %d (%d KB)",menu_debug_memory_zone_size,menu_debug_memory_zone_size/1024);
		//menu_escribe_linea_opcion(linea++,-1,1,textoshow);
		zxvision_print_string_defaults_fillspc(ventana,1,linea++,textoshow);


		char subzone_info[33];
		machine_get_memory_subzone_name(menu_debug_memory_zone,current_machine_type, menu_debug_hexdump_direccion, subzone_info);
		if (subzone_info[0]!=0) {
			sprintf(buffer_linea," S~~ubzone info: %s",subzone_info);
			zxvision_print_string_defaults_fillspc(ventana,1,linea++,buffer_linea);
		}
		else {
			zxvision_print_string_defaults_fillspc(ventana,1,linea++,"");
		}


		//Restaurar comportamiento atajos
		menu_writing_inverse_color.v=antes_menu_writing_inverse_color.v;

    return linea;
}

void menu_debug_hexdump_mostrar_cursor(zxvision_window *ventana)
{
		//Mostrar cursor si en modo edicion

		//Al mostrar en cursor: si esta en parte ascii, hacer parpadear el caracter en esa zona, metiendo color de opcion seleccionada
		//Si esta en parte hexa, parpadeamos la parte del nibble que editamos, el otro nibble no parpadea. Ambos tienen color de opcion seleccionada
		//Si multitarea esta a off, no existe el parpadeo, y por tanto, para que se viera en que nibble edita, se mostrara el caracter _, logicamente
		//tapando el caracter de debajo
		//Para ver los caracteres de debajo, los asignamos antes, en el bucle que hace el volcado hexa, y lo guardo en las variables
		//nibble_char_cursor (que dice el caracter de debajo del cursor) y nibble_char (que dice el otro caracter que acompanya al nibble)


		if (menu_hexdump_edit_mode) {
			int xfinal=7+menu_hexdump_edit_position_x;
			int yfinal=2+menu_hexdump_edit_position_y;

			menu_debug_hexdump_print_editcursor(ventana,xfinal,yfinal,menu_hexdump_nibble_char_cursor);

			//Indicar nibble entero. En caso de edit hexa
			if (!menu_hexdump_editando_en_zona_ascii) {
				xfinal=7+menu_hexdump_edit_position_x_nibble;
				menu_debug_hexdump_print_editcursor_nibble(ventana,xfinal,yfinal,menu_hexdump_nibble_char);
			}
		}
}

char *menu_debug_hexdump_change_ptr_historial[UTIL_SCANF_HISTORY_MAX_LINES]={
    NULL
};

menu_z80_moto_int menu_debug_hexdump_change_pointer(menu_z80_moto_int p)
{


    char string_address[10];

    sprintf (string_address,"%XH",p);


    //menu_ventana_scanf("Address?",string_address,10);
    int tecla=zxvision_scanf_history("Address?",string_address,10,menu_debug_hexdump_change_ptr_historial);

    //No sale con ESC
    if (tecla!=2) {
        //Evaluar la dirección como una expresión, así podemos usar registros, sumas, etc
        menu_debug_cpu_calculate_expression(string_address,&p);
    }


    return p;

}


int menu_debug_hexdump_inicializada_follow_expression=0;

//Retorna 1 si está definida la expresión
int menu_debug_hexdump_follow_expression_defined(void)
{
    if (menu_debug_hexdump_inicializada_follow_expression) {
        if (menu_hexdump_follow_expression[0].tipo!=TPT_FIN) {
            return 1;
        }
    }

    return 0;
}

void menu_debug_hexdump_get_follow_expression_string(char *string_texto)
{
    if (menu_debug_hexdump_follow_expression_defined() ) {
        exp_par_tokens_to_exp(menu_hexdump_follow_expression,string_texto,MAX_PARSER_TOKENS_NUM);
    }

    else {
        string_texto[0]=0;
    }
}


int menu_debug_hexdump_add_follow_expression(char *string_texto)
{
    int result=exp_par_exp_to_tokens(string_texto,menu_hexdump_follow_expression);

    if (result<0) {
        debug_printf(VERBOSE_ERR,"Error adding follow expression");
        menu_hexdump_follow_expression[0].tipo=TPT_FIN;
    }
    else {
        menu_debug_hexdump_inicializada_follow_expression=1;
    }


    return result;
}

void menu_debug_hexdump_follow(void)
{

    if (menu_hexdump_follow_mode) {
        menu_hexdump_follow_mode=0;
    }

    else {

        char string_texto[MAX_BREAKPOINT_CONDITION_LENGTH];
        char copia_string_texto[MAX_BREAKPOINT_CONDITION_LENGTH];

        exp_par_tokens_to_exp(menu_hexdump_follow_expression,string_texto,MAX_PARSER_TOKENS_NUM);
        strcpy(copia_string_texto,string_texto);

        menu_ventana_scanf("Expression to follow",string_texto,MAX_BREAKPOINT_CONDITION_LENGTH);

        int result=menu_debug_hexdump_add_follow_expression(string_texto);
        if (result<0) {
            //Dejamos la expresion anterior
            exp_par_exp_to_tokens(copia_string_texto,menu_hexdump_follow_expression);
        }

        else {
            menu_hexdump_follow_mode=1;
        }

    }

    //Si se ha generado error al agregar expresión
    //comprobar error
    if (if_pending_error_message) {
        menu_muestra_pending_error_message();
    }


}

zxvision_window zxvision_window_debug_hexdump;


void menu_debug_hexdump(MENU_ITEM_PARAMETERS)
{
	menu_espera_no_tecla();
	menu_reset_counters_tecla_repeticion();

    if (!menu_debug_hexdump_inicializada_follow_expression) {
        menu_debug_hexdump_inicializada_follow_expression=1;
        menu_hexdump_follow_expression[0].tipo=TPT_FIN;
    }

	zxvision_window *ventana;
    ventana=&zxvision_window_debug_hexdump;

    //IMPORTANTE! no crear ventana si ya existe. Esto hay que hacerlo en todas las ventanas que permiten background.
    //si no se hiciera, se crearia la misma ventana, y en la lista de ventanas activas , al redibujarse,
    //la primera ventana repetida apuntaria a la segunda, que es el mismo puntero, y redibujaria la misma, y se quedaria en bucle colgado
    //zxvision_delete_window_if_exists(ventana);

    //Crear ventana si no existe
    if (!zxvision_if_window_already_exists(ventana)) {

        int xventana,yventana,ancho_ventana,alto_ventana,is_minimized,is_maximized,ancho_antes_minimize,alto_antes_minimize;
        //no usamos ancho_antes_minimize ni alto_antes_minimize porque usamos zxvision_new_window_nocheck_staticsize en vez de zxvision_new_window_gn_cim


        if (!util_find_window_geometry("hexeditor",&xventana,&yventana,&ancho_ventana,&alto_ventana,&is_minimized,&is_maximized,&ancho_antes_minimize,&alto_antes_minimize)) {
            //xventana=DEBUG_HEXDUMP_WINDOW_X;
            //yventana=DEBUG_HEXDUMP_WINDOW_Y;
            ancho_ventana=DEBUG_HEXDUMP_WINDOW_ANCHO;
            alto_ventana=DEBUG_HEXDUMP_WINDOW_ALTO;
            xventana=menu_center_x()-ancho_ventana/2;
            yventana=menu_center_y()-alto_ventana/2;
        }

        //guardar tamanyo inicial para cuando se recrea la ventana indicarlo como tamanyo de antes minimizado
        //int ancho_ventana_inicial=ancho_ventana;
        //int alto_ventana_inicial=alto_ventana;


        //asignamos mismo ancho visible que ancho total para poder usar la ultima columna de la derecha, donde se suele poner scroll vertical
        //zxvision_new_window_nocheck_staticsize(ventana,x,y,ancho,alto,ancho,alto-2,"Hexadecimal Editor");
        menu_debug_hexdump_crea_ventana(ventana,xventana,yventana,ancho_ventana,alto_ventana,is_minimized,is_maximized,ancho_antes_minimize,alto_antes_minimize);

    }

    //Si ya existe, activar esta ventana
    else {
        zxvision_activate_this_window(ventana);
    }

    zxvision_draw_window(ventana);

    //El overlay de esta ventana refresca la vista hexadecimal continuamente
    //desde el bucle principal tambien se refresca la vista, aunque solo cuando se pulsa una tecla
    //Esto permite que si estamos dentro de la ventana y la memoria cambia, se refleje al momento
    menu_debug_hexdump_overlay_window=ventana;
    zxvision_set_window_overlay(ventana,menu_debug_hexdump_overlay);


    if (zxvision_currently_restoring_windows_on_start) {
        //printf ("Saliendo de ventana ya que la estamos restaurando en startup\n");
        return;
    }


    int alto_anterior;
    int ancho_anterior;

    zxvision_window_save_size(ventana,&ancho_anterior,&alto_anterior);

	//ventana->can_use_all_width=1; //Para poder usar la ultima columna de la derecha donde normalmente aparece linea scroll



    z80_byte tecla;

	int salir=0;

	menu_hexdump_valor_xor=0;



	if (MACHINE_IS_ZX80_TYPE) menu_debug_hexdump_with_ascii_modo_ascii=1;
	else if (MACHINE_IS_ZX81_TYPE) menu_debug_hexdump_with_ascii_modo_ascii=2;

	else menu_debug_hexdump_with_ascii_modo_ascii=0;


	int asked_about_writing_rom=0;


    int redibujar_ventana=1;


    do {
        //printf("bucle do %d\n",contador_segundo);

        int bytes_por_ventana=menu_hexdump_bytes_por_linea*menu_hexdump_lineas_total;

        /*
        IMPORTANTE: todo este bucle se repite continuamente si hay un evento de raton (mover raton, mover ventana, redimensionar...)
        por tanto, se ejecutaria continuamente esto, gastando monton de cpu, saturandola, y haciendo framedrop y no se veria
        ni la ventana moviendose ni nada de la interfaz
        Por tanto lo que hacemos es que solo se reescribe el contenido de la ventana y se redibuja cuando realmente hay un cambio
        (pulsada tecla o redimensionado de ventana)
        Esto puede que suceda en alguna otra ventana (aunque creo que no)
        Si es asi, que no refresca casi la ventana en estos casos, el truco es el mismo que aqui
        Creo que esta es de las pocas (o casi ninguna) ventana que sucede pues se realiza mucha escritura de algo y no viene por funcion de overlay
        */

        if (redibujar_ventana) {

            //printf ("dibujar ventana. contador: %d\n",++temp_xxxx);

        //long tiempo_inicio_bucle=timer_get_current_useconds();

        //printf("bucle do. contador segundo %d\n",contador_segundo);
		menu_hexdump_lineas_total=ventana->visible_height-10;

        if (menu_hexdump_lineas_total<3) menu_hexdump_lineas_total=3;


        menu_debug_hexdump_cursor_en_zona_ascii=0;
        menu_hexdump_editando_en_zona_ascii=0;


        //Si maquina no es QL, direccion siempre entre 0 y 65535
        //menu_debug_hexdump_direccion=adjust_address_space_cpu(menu_debug_hexdump_direccion);
        menu_debug_hexdump_direccion=adjust_address_memory_size(menu_debug_hexdump_direccion);


		int linea=0;





		//Antes de escribir, normalizar zona memoria
		menu_debug_set_memory_zone_attr();

        char textoshow[33];

		sprintf (textoshow,"Showing %d bytes per page:",bytes_por_ventana);

		zxvision_print_string_defaults_fillspc(ventana,1,linea++,textoshow);

		zxvision_print_string_defaults_fillspc(ventana,1,linea++,"");




		//Donde esta el otro caracter que acompanya al nibble, en caso de cursor en zona hexa
		menu_hexdump_edit_position_x_nibble=menu_hexdump_edit_position_x^1;


		if (menu_hexdump_edit_position_x>menu_hexdump_bytes_por_linea*2) menu_debug_hexdump_cursor_en_zona_ascii=1;


		if (menu_hexdump_edit_mode && menu_debug_hexdump_cursor_en_zona_ascii) menu_hexdump_editando_en_zona_ascii=1;

		menu_hexdump_nibble_char='X';
		menu_hexdump_nibble_char_cursor='X';



        //Inicio Render

        linea=menu_hexdump_print_hexa_ascii(ventana,linea);

        //Fin Render

        menu_debug_hexdump_mostrar_cursor(ventana);



        linea=menu_debux_hexdump_leyenda(ventana,linea);



        //printf ("dibujar ventana. contador_segundo: %d\n",contador_segundo);

        //
		zxvision_draw_window_contents(ventana);

        }

		//NOTA: este menu no acostumbra a refrescar rapido la ventana cuando la redimensionamos con el raton
		//es una razon facil: el volcado de hexa usa relativamente mucha cpu,
		//cada vez que redimensionamos ventana, se llama al bucle continuamente, usando mucha cpu y si esta el autoframeskip,
		//hace saltar frames

        //printf("(%ld)\n",timer_get_current_useconds()-tiempo_inicio_bucle);
		tecla=zxvision_common_getkey_refresh();
        //printf("(%ld)\n",timer_get_current_useconds()-tiempo_inicio_bucle);

        //menu_refresca_pantalla();


        //Aviso: hay que conseguir que las letras de accion no esten entre la a-f, porque asi,
        //podemos usar dichas letras para editar hexa

        if (!tecla) redibujar_ventana=0;
        else redibujar_ventana=1;

        switch (tecla) {

            case 11:
                menu_debug_hexdump_cursor_arriba();

            break;

            case 10:
                menu_debug_hexdump_cursor_abajo();

            break;

            case 8:
            case 12:
                //izquierda o delete
                if (menu_hexdump_edit_mode) {
                    //if (menu_hexdump_edit_position_x>0) menu_hexdump_edit_position_x--;
                    menu_debug_hexdump_edit_cursor_izquierda();
                }
            break;

            case 9:
                //derecha
                if (menu_hexdump_edit_mode) {
                    menu_debug_hexdump_edit_cursor_derecha(0);
                    //if (menu_hexdump_edit_position_x<(bytes_por_linea*2)-1) menu_hexdump_edit_position_x++;
                }
            break;

            case 24:
                //PgUp
                menu_debug_hexdump_direccion -=bytes_por_ventana;
                menu_debug_hexdump_direccion=menu_debug_hexdump_adjusta_en_negativo(menu_debug_hexdump_direccion,bytes_por_ventana);
            break;

            case 25:
                //PgDn
                menu_debug_hexdump_direccion +=bytes_por_ventana;
            break;

            case 'm':
                if (!menu_hexdump_editando_en_zona_ascii)  {
                    menu_debug_hexdump_direccion=menu_debug_hexdump_change_pointer(menu_debug_hexdump_direccion);
                    //menu_debug_hexdump_ventana();
                    zxvision_draw_window(ventana);
                    //Y resetear cursor edicion
                    menu_hexdump_edit_position_x=0;
                    menu_hexdump_edit_position_y=0;
                }
            break;

            case 'o':
                if (!menu_hexdump_editando_en_zona_ascii)  {
                    menu_debug_hexdump_copy();
                    //menu_debug_hexdump_ventana();
                    zxvision_draw_window(ventana);
                }
            break;

            case 's':
                if (!menu_hexdump_editando_en_zona_ascii)  {
                    menu_debug_hexdump_shift_bits();
                    //menu_debug_hexdump_ventana();
                    zxvision_draw_window(ventana);
                }
            break;

            case 'h':
                if (!menu_hexdump_editando_en_zona_ascii)  {
                    menu_debug_hexdump_with_ascii_modo_ascii++;
                    if (menu_debug_hexdump_with_ascii_modo_ascii==3) menu_debug_hexdump_with_ascii_modo_ascii=0;
                }
            break;

            case 'i':
                if (!menu_hexdump_editando_en_zona_ascii) menu_hexdump_valor_xor ^= 255;
            break;

            case 't':
                if (!menu_hexdump_editando_en_zona_ascii) {
                    menu_hexdump_edit_mode ^= 1;
                    menu_espera_no_tecla();
                    tecla=0; //para no enviar dicha tecla al editor
                }

                //Si zona de filemem
                if (menu_hexdump_edit_mode && menu_debug_memory_zone==MEMORY_ZONE_NUM_FILE_ZONE) {
                    menu_debug_hexdump_aviso_edit_filezone(ventana);
                }
            break;

            case 'u':
                //Ver info subzonas
                menu_debug_hexdump_info_subzones();
            break;

            //case 'l':
            //	menu_debug_hex_shows_inves_low_ram.v ^=1;
            //break;

            case 'l':
                menu_debug_hexdump_follow();
            break;

            case 'z':

                if (!menu_hexdump_editando_en_zona_ascii) {
                    menu_debug_change_memory_zone();
                    asked_about_writing_rom=0;
                }

                break;

            //Salir con ESC si no es modo edit
            case 2:
                if (menu_hexdump_edit_mode) {
                    menu_hexdump_edit_mode=0;
                }
                else salir=1;
            break;

            //salir con tecla background
            case 3:
                salir=1;
            break;

            //Enter tambien sale de modo edit
            case 13:
                if (menu_hexdump_edit_mode) menu_hexdump_edit_mode=0;
            break;



        }

        //Y ahora para el caso de edit_mode y pulsar tecla hexa o ascii segun la zona
        int editar_byte=0;
        if (menu_hexdump_edit_mode) {
            //Para la zona ascii
            if (menu_debug_hexdump_cursor_en_zona_ascii && tecla>=32 && tecla<=126) editar_byte=1;

            //Para la zona hexa
            if (
                !menu_debug_hexdump_cursor_en_zona_ascii &&
                ( (tecla>='0' && tecla<='9') || (tecla>='a' && tecla<='f') )
                ) {
                editar_byte=1;
            }
        }

        //Ver si vamos a editar en zona de rom
        if (editar_byte) {
            int attribute_zone;
            //Si zona por defecto mapped memory, asumimos lectura/escritura
            if (menu_debug_memory_zone==-1) attribute_zone=3;
            else machine_get_memory_zone_attrib(menu_debug_memory_zone, &attribute_zone);

            //printf ("Attrib zone %d asked %d\n",attribute_zone,asked_about_writing_rom);

            //Attrib: bit 0: read, bit 1: write
            //Si no tiene atributo de escritura y no se ha pedido antes si se quiere escribir en rom
            if ( (attribute_zone&2)==0 && !asked_about_writing_rom) {
                if (menu_confirm_yesno_texto("Memory zone is ROM","Sure you want to edit?")==0) {
                    editar_byte=0;
                }
                else {
                    asked_about_writing_rom=1;
                }

                //Volver a dibujar ventana, pues se ha borrado al pregutar confirmacion
                //menu_debug_hexdump_ventana();
                zxvision_draw_window(ventana);
            }


        }


        //asked_about_writing_rom

        if (editar_byte) {
                menu_z80_moto_int direccion_cursor=menu_debug_hexdump_get_cursor_pointer();


                //TODO: ver si se sale de tamanyo zona memoria

                //printf ("Direccion edicion: %X\n",direccion_cursor);

                //Obtenemos byte en esa posicion
                z80_byte valor_leido=menu_debug_get_mapped_byte(direccion_cursor);


                //Estamos en zona hexa o ascii

                if (!menu_debug_hexdump_cursor_en_zona_ascii) {
                    //printf ("Zona hexa\n");
                    //Zona hexa

                    //Obtener valor nibble
                    z80_byte valor_nibble;

                    if (tecla>='0' && tecla<='9') valor_nibble=tecla-'0';
                    else valor_nibble=tecla-'a'+10;

                    //Ver si par o impar
                    if ( (menu_hexdump_edit_position_x %2) ==0) {
                        //par. alterar nibble alto
                        valor_leido=(valor_leido&0x0F) | (valor_nibble<<4);
                    }

                    else {
                        //impar. alterar nibble bajo
                        valor_leido=(valor_leido&0xF0) | valor_nibble;
                    }
                }

                else {
                    //printf ("Zona ascii\n");
                    valor_leido=tecla;

                    //En el caso de zx80/81

                    if (menu_debug_hexdump_with_ascii_modo_ascii==1) valor_leido=ascii_to_zx80(valor_leido);
                    if (menu_debug_hexdump_with_ascii_modo_ascii==2) valor_leido=ascii_to_zx81(valor_leido);
                }



                //Escribimos valor
                menu_debug_write_mapped_byte(direccion_cursor,valor_leido);

                //Y mover cursor a la derecha
                menu_debug_hexdump_edit_cursor_derecha(1);

                //Si se llega a detecha de hexa o ascii, saltar linea


        }

		//Si ha cambiado el alto
        //Redibujar ventana, aunque no recrearla, pues zxvision ya recrea la ventana al ampliarla

		int alto_ventana=ventana->visible_height;
		int ancho_ventana=ventana->visible_width;
		//xventana=ventana->x;
		//yventana=ventana->y;
		if (alto_ventana!=alto_anterior || ancho_ventana!=ancho_anterior) {
			//printf ("recrear ventana. contador_segundo: %d\n",contador_segundo);
			//Recrear ventana
			//Cancelamos edicion si estaba ahi
			menu_hexdump_editando_en_zona_ascii=0;
			menu_hexdump_edit_mode=0;
			menu_hexdump_edit_position_x=0;
			menu_hexdump_edit_position_y=0;
            //int is_minimized=ventana->is_minimized;

			//zxvision_destroy_window(ventana);
			//menu_debug_hexdump_crea_ventana(ventana,xventana,yventana,ancho_ventana,alto_ventana,is_minimized,ancho_antes_minimize,alto_antes_minimize);

            //Indicar tamanyo de antes minimizado, que es el que tenia al inicio
            //dado que se recrea la ventana siempre que cambia tamaño (y si se minimiza tambien),
            //queremos que se indique el tamaño que tenia antes de minimizar por si se deshace el minimizado
            //printf("--menu_debug_hexdump: setting size before minimize: %d X %d\n",alto_ventana_inicial,ancho_ventana_inicial);
            //ventana->height_before_max_min_imize=alto_ventana_inicial;
            //ventana->width_before_max_min_imize=ancho_ventana_inicial;
            zxvision_window_save_size(ventana,&ancho_anterior,&alto_anterior);

			//alto_anterior=alto_ventana;
			//ancho_anterior=ancho_ventana;
            //printf ("ventana recreada. contador_segundo: %d\n",contador_segundo);

            redibujar_ventana=1;
		}




    } while (salir==0);



	//Grabar geometria ventana
	util_add_window_geometry_compact(ventana);



	if (tecla==3) {
		zxvision_message_put_window_background();
	}

	else {
		zxvision_destroy_window(ventana);
 	}


}



//Entrada seleccionada
int adventure_keyboard_selected_item=0;

//Posicion dentro del string
int adventure_keyboard_index_selected_item=0;

//z80_bit menu_osd_adventure_sending_keys={0};


//+1 para poder agregar espacio al final o no
char osd_adventure_palabra_enviada[MAX_OSD_ADV_KEYB_TEXT_LENGTH+1]="";



void menu_osd_adventure_kb_press_key_variable(char letra)
{
	if (letra==0) return; //pequenyo bug: si acaba texto con ~~ no se abrira luego de nuevo el menu. Bug???

	//printf ("Pulsar tecla entrada %d indice en entrada: %d letra: %c\n",adventure_keyboard_selected_item,adventure_keyboard_index_selected_item,letra);

	debug_printf (VERBOSE_DEBUG,"Pressing key %c of word %s",letra,osd_adventure_palabra_enviada);

	//Espacio no la gestiona esta funcion de convert_numeros_...
	if (letra==' ') util_set_reset_key(UTIL_KEY_SPACE,1);
	//else convert_numeros_letras_puerto_teclado_continue(letra,1);
	else ascii_to_keyboard_port(letra);

	//Lanzar pulsar tecla
	timer_on_screen_adv_key=adventure_keyboard_key_length;
}

void menu_osd_adventure_kb_press_key(void)
{

	//Aunque el usuario haya puesto alguna mayuscula, metemos minusculas
	char letra;

	//Ignorar ~~

	do {
		letra=letra_minuscula(osd_adventure_palabra_enviada[adventure_keyboard_index_selected_item]);
		if (letra=='~') adventure_keyboard_index_selected_item++;
	} while (letra=='~' && letra!=0);

	menu_osd_adventure_kb_press_key_variable(letra);

	/*if (letra==0) return; //pequenyo bug: si acaba texto con ~~ no se abrira luego de nuevo el menu. Bug???

	//printf ("Pulsar tecla entrada %d indice en entrada: %d letra: %c\n",adventure_keyboard_selected_item,adventure_keyboard_index_selected_item,letra);

	debug_printf (VERBOSE_DEBUG,"Pressing key %c of word %s",letra,osd_adv_kbd_list[adventure_keyboard_selected_item]);

	//Espacio no la gestiona esta funcion de convert_numeros_...
	if (letra==' ') util_set_reset_key(UTIL_KEY_SPACE,1);
	//else convert_numeros_letras_puerto_teclado_continue(letra,1);
	else ascii_to_keyboard_port(letra);

	//Lanzar pulsar tecla
	timer_on_screen_adv_key=adventure_keyboard_key_length; */

}



void menu_osd_adventure_keyboard_action(MENU_ITEM_PARAMETERS)
{
	//printf ("opcion seleccionada: %d\n",valor_opcion);
	adventure_keyboard_selected_item=valor_opcion;
    adventure_keyboard_index_selected_item=0;


    //Si enviado enter, espacio o comillas
    if (adventure_keyboard_selected_item<0) {
        switch(adventure_keyboard_selected_item) {
            case -1:
                //printf("Enviamos enter\n");
                sprintf(osd_adventure_palabra_enviada,"%c",13);
            break;

            case -2:
                //printf("Enviamos espacio\n");
                sprintf(osd_adventure_palabra_enviada,"%c",32);
            break;

            case -3:
                //printf("Enviamos comillas\n");
                sprintf(osd_adventure_palabra_enviada,"%c",34);
            break;
        }
    }

	else {


        //Estamos enviando teclas
        //menu_osd_adventure_sending_keys.v=1;

        //Metemos texto en cadena temporal
        //Ver si habia que enviar un espacio al final
        if (adventure_keyboard_send_final_spc) {
            sprintf(osd_adventure_palabra_enviada,"%s ",osd_adv_kbd_list[adventure_keyboard_selected_item]);
        }
        else {
            strcpy(osd_adventure_palabra_enviada,osd_adv_kbd_list[adventure_keyboard_selected_item]);
        }

    }

	menu_osd_adventure_kb_press_key();
}

//Retorno desde el core
void menu_osd_adventure_keyboard_next(void)
{

    //printf("Caracter: %d\n",osd_adventure_palabra_enviada[adventure_keyboard_index_selected_item]);

    //Si final de string
    adventure_keyboard_index_selected_item++;

	if (osd_adventure_palabra_enviada[adventure_keyboard_index_selected_item]==0) {
		//printf ("Fin texto\n");

        //reabrir el menu
        menu_osd_adventure_keyboard(0);
        return;

	}

	//Siguiente tecla
	else menu_osd_adventure_kb_press_key();
}


#define ADVENTURE_KB_X (menu_origin_x() )
#define ADVENTURE_KB_Y 0

//Le ponemos maximo ancho 32 que es el mismo que gestiona la funcion de dibujar menu
#define ADVENTURE_KB_ANCHO 32

//Le ponemos maximo alto 24 que es el mismo que gestiona la funcion de dibujar menu
#define ADVENTURE_KB_ALTO 24

//maximo de alto total admitido para la ventana
#define ADVENTURE_KB_MAX_TOTAL_HEIGHT 500

//conservar valor de scroll ultimo para que cuando listado sea grande,
//poder conservar ultima posicion
int menu_osd_advkb_last_offset_y=0;

void menu_osd_adventure_keyboard(MENU_ITEM_PARAMETERS)
{

	//Si estamos enviando teclas
	//if (menu_osd_adventure_sending_keys.v) {
	//	menu_osd_adventure_keyboard_next();
	//	return;
	//}

	//Si lista vacia, error
	if (osd_adv_kbd_defined==0) {
		debug_printf (VERBOSE_ERR,"Empty list");
		return;
	}

	//Si estamos enviando teclas, desactivar
	timer_on_screen_adv_key=0;



 	menu_espera_no_tecla();
	menu_reset_counters_tecla_repeticion();

	zxvision_window ventana;

	zxvision_new_window(&ventana,ADVENTURE_KB_X,ADVENTURE_KB_Y,ADVENTURE_KB_ANCHO,ADVENTURE_KB_ALTO,
							ADVENTURE_KB_ANCHO-1,ADVENTURE_KB_MAX_TOTAL_HEIGHT,"OSD Adventure Keyboard");
	zxvision_draw_window(&ventana);

//printf ("ancho: %d\n",ADVENTURE_KB_ANCHO);


        menu_item *array_menu_osd_adventure_keyboard;
        menu_item item_seleccionado;
        int retorno_menu;
        do {

		int initial_test; //si es 1, haremos el calculo inicial del alto

		int alto_ventana=ADVENTURE_KB_ALTO;
		int y_ventana=ADVENTURE_KB_Y;

		for (initial_test=1;initial_test>=0;initial_test--) {


          //Hay que redibujar la ventana desde este bucle
          if (!initial_test) {
			  //menu_dibuja_ventana(ADVENTURE_KB_X,y_ventana,ADVENTURE_KB_ANCHO,alto_ventana,"OSD Adventure Keyboard");
			  zxvision_set_y_position(&ventana,y_ventana);
			  zxvision_set_visible_height(&ventana,alto_ventana);

			  //Alteramos alto total para que coincida con alto ventana (siempre que sea menor que el alto actual)
			  //si fuese mayor el alto, estariamos necesitando mas memoria y seria un problema
			  //esto es un poco feo realmente, pero bueno, al reducir el tamaño no hay problema de que nos salgamos de la memoria
			  int current_height=ventana.total_height;
			  int desired_height=alto_ventana-2;

			  if (desired_height<current_height) {
				  ventana.total_height=desired_height;
			  }
		  }


        //Como no sabemos cual sera el item inicial, metemos este sin asignar, que se sobreescribe en el siguiente menu_add_item_menu
        menu_add_item_menu_inicial(&array_menu_osd_adventure_keyboard,"",MENU_OPCION_UNASSIGNED,NULL,NULL);

	//if (osd_adv_kbd_list[adventure_keyboard_selected_item][adventure_keyboard_index_selected_item]==0) {
	//osd_adv_kbd_defined


        //Metemos enter y espacio
        menu_add_item_menu_format(array_menu_osd_adventure_keyboard,MENU_OPCION_NORMAL,menu_osd_adventure_keyboard_action,NULL,"<Enter>");
        menu_add_item_menu_tabulado(array_menu_osd_adventure_keyboard,1,0);
        menu_add_item_menu_valor_opcion(array_menu_osd_adventure_keyboard,-1); //-1 identifica un enter

        menu_add_item_menu_format(array_menu_osd_adventure_keyboard,MENU_OPCION_NORMAL,menu_osd_adventure_keyboard_action,NULL,"<Space>");
        menu_add_item_menu_tabulado(array_menu_osd_adventure_keyboard,9,0);
        menu_add_item_menu_valor_opcion(array_menu_osd_adventure_keyboard,-2); //-2 identifica un space

        menu_add_item_menu_format(array_menu_osd_adventure_keyboard,MENU_OPCION_NORMAL,menu_osd_adventure_keyboard_action,NULL,"<\">");
        menu_add_item_menu_tabulado(array_menu_osd_adventure_keyboard,17,0);
        menu_add_item_menu_valor_opcion(array_menu_osd_adventure_keyboard,-3); //-3 identifica comillas

		int i;
		int last_x=1;
		int last_y=1;
		int salir=0;


		//Asignar hotkeys, segun si se han asignado antes o no
		//int hotkeys_assigned[26]; //de la A a la Z
		//for (i=0;i<26;i++) hotkeys_assigned[i]=0;


		for (i=0;i<osd_adv_kbd_defined && !salir;i++) {
			int longitud_texto=strlen(osd_adv_kbd_list[i])+1; //Espacio para la entrada y 1 espacio
			if (last_x+longitud_texto>ADVENTURE_KB_ANCHO) {
				last_x=1;
				last_y++;
			}

			//controlar maximo de alto
			if (last_y>=ADVENTURE_KB_MAX_TOTAL_HEIGHT) {
				debug_printf (VERBOSE_DEBUG,"Reached maximum window height");
				last_y--;
				salir=1;
			}

			else {
				//Si es cadena vacia, ignorarla. No deberia pasar pues se debe denegar desde donde se lee la configuracion, pero por si acaso
				if (osd_adv_kbd_list[i][0]==0) {
					debug_printf (VERBOSE_DEBUG,"Null string at %d",i);
				}

				else {

					int tiene_hotkey=0;

					char texto_opcion[64];
					strcpy(texto_opcion,osd_adv_kbd_list[i]);

					char hotkey;

					//Caracter de hotkey. Crearlo automaticamente
					//hotkey=letra_minuscula(osd_adv_kbd_list[i][0]);

					//Caracter de hotkey. Dejar que el usuario lo escriba en la cadena de texto. Ver si dicha cadena lo tiene

					int j;
					for (j=0;texto_opcion[j];j++) {
						if (texto_opcion[j]=='~' && texto_opcion[j+1]=='~') {
							//Si hay letra detras
							hotkey=letra_minuscula(texto_opcion[j+2]);
							if (hotkey) tiene_hotkey=1;
						}
					}


				    menu_add_item_menu_format(array_menu_osd_adventure_keyboard,MENU_OPCION_NORMAL,menu_osd_adventure_keyboard_action,NULL,texto_opcion);
        		    menu_add_item_menu_tabulado(array_menu_osd_adventure_keyboard,last_x,last_y);
					menu_add_item_menu_valor_opcion(array_menu_osd_adventure_keyboard,i);
					//printf ("Agregando palabra %s en %d,%d\n",texto_opcion,last_x,last_y);

					if (tiene_hotkey) {
						menu_add_item_menu_shortcut(array_menu_osd_adventure_keyboard,hotkey);
						longitud_texto -=2;
					}

				}

				last_x+=longitud_texto;
			}

		}

		//Recalcular alto, y_inivial
		//del alto, se pierden 2 siempre
		//si tuvieramos el maximo de y, valdria 21. Y el maximo de alto es 24
		//printf ("ultima y: %d\n",last_y);
		alto_ventana=last_y+3;
		y_ventana=menu_center_y()-alto_ventana/2;
		if (y_ventana<0) y_ventana=0;


		}


		//Recuperamos antiguo offset de ventana
		zxvision_set_offset_y(&ventana,menu_osd_advkb_last_offset_y);


		//Nombre de ventana solo aparece en el caso de stdout
        retorno_menu=menu_dibuja_menu_no_title_lang(&osd_adventure_keyboard_opcion_seleccionada,&item_seleccionado,array_menu_osd_adventure_keyboard,"OSD Adventure KB" );


	//En caso de menus tabulados, es responsabilidad de este de borrar la ventana

                if ((item_seleccionado.tipo_opcion&MENU_OPCION_ESC)==0 && retorno_menu>=0) {
                        //llamamos por valor de funcion
                        if (item_seleccionado.menu_funcion!=NULL) {
				//printf ("Item seleccionado: %d\n",item_seleccionado.valor_opcion);
                                //printf ("actuamos por funcion\n");

	                        salir_todos_menus=1;

                                item_seleccionado.menu_funcion(item_seleccionado.valor_opcion);


                        }
                }

        } while ( (item_seleccionado.tipo_opcion&MENU_OPCION_ESC)==0 && retorno_menu!=MENU_RETORNO_ESC && !salir_todos_menus);


		//Guardamos offset de ventana actual
		menu_osd_advkb_last_offset_y=ventana.offset_y;


		//menu_espera_no_tecla();

		//menu_abierto=1;
		//Si con control de joystick se ha salido con tecla ESCMenu, esa tecla de joystick lo que hace es ESC
		//pero luego fuerza a abrir el menu de nuevo. Por tanto, decimos que no hay que abrir menu
		menu_event_open_menu.v=0;

		//printf ("en final de funcion\n");
		zxvision_destroy_window(&ventana);

}

int menu_debug_dma_existe_dma=0;


void menu_debug_dma_draw(zxvision_window *w)
{

    zxvision_window *menu_debug_dma_tsconf_zxuno_overlay_window=w;

	char nombre_dma[33];
	//por defecto por si acaso
	strcpy(nombre_dma,"DMA");

	if (MACHINE_IS_ZXUNO) {
		strcpy(nombre_dma,"ZXUNO DMA");
		//alto_ventana++;
	}

	if (MACHINE_IS_TSCONF) strcpy(nombre_dma,"TSConf DMA");

	if (datagear_dma_emulation.v) strcpy(nombre_dma,"Datagear DMA");


    int linea=0;

    //primera linea para tipo dma
    linea++;

    //Asumimos no existe
    menu_debug_dma_existe_dma=0;

	char texto_dma[33];

	if (datagear_dma_emulation.v) {
        menu_debug_dma_existe_dma=1;

		//NOTA: Si se activa datagear, no se vera si hay dma de tsconf o zxuno
		z80_int dma_port_a=value_8_to_16(datagear_port_a_start_addr_high,datagear_port_a_start_addr_low);
		z80_int dma_port_b=value_8_to_16(datagear_port_b_start_addr_high,datagear_port_b_start_addr_low);

		z80_int dma_len=value_8_to_16(datagear_block_length_high,datagear_block_length_low);

		sprintf (texto_dma,"Port A:      %04XH",dma_port_a);
		//menu_escribe_linea_opcion(linea++,-1,1,texto_dma);
		zxvision_print_string_defaults_fillspc(menu_debug_dma_tsconf_zxuno_overlay_window,1,linea++,texto_dma);

		sprintf (texto_dma,"Port B:      %04XH",dma_port_b);
		//menu_escribe_linea_opcion(linea++,-1,1,texto_dma);
		zxvision_print_string_defaults_fillspc(menu_debug_dma_tsconf_zxuno_overlay_window,1,linea++,texto_dma);

		sprintf (texto_dma,"Length:      %5d",dma_len);
		//menu_escribe_linea_opcion(linea++,-1,1,texto_dma);
		zxvision_print_string_defaults_fillspc(menu_debug_dma_tsconf_zxuno_overlay_window,1,linea++,texto_dma);

		if (datagear_wr0 & 4) sprintf (texto_dma,"Port A->B");
		else sprintf (texto_dma,"Port B->A");

		//menu_escribe_linea_opcion(linea++,-1,1,texto_dma);
		zxvision_print_string_defaults_fillspc(menu_debug_dma_tsconf_zxuno_overlay_window,1,linea++,texto_dma);



		char access_type[20];

        if (datagear_wr1 & 8) sprintf (access_type,"I/O");
		else sprintf (access_type,"Memory");

		if ( (datagear_wr1 & 32) == 0 ) {
            if (datagear_wr1 & 16) sprintf (texto_dma,"Port A++. %s",access_type);
            else sprintf (texto_dma,"Port A--. %s",access_type);
        }
		else sprintf (texto_dma,"Port A fixed. %s",access_type);
		//menu_escribe_linea_opcion(linea++,-1,1,texto_dma);
		zxvision_print_string_defaults_fillspc(menu_debug_dma_tsconf_zxuno_overlay_window,1,linea++,texto_dma);


        if (datagear_wr2 & 8) sprintf (access_type,"I/O");
		else sprintf (access_type,"Memory");

		if ( (datagear_wr2 & 32) == 0 ) {
            if (datagear_wr2 & 16) sprintf (texto_dma,"Port B++. %s",access_type);
            else sprintf (texto_dma,"Port B--. %s",access_type);
        }
		else sprintf (texto_dma,"Port B fixed. %s",access_type);
		//menu_escribe_linea_opcion(linea++,-1,1,texto_dma);
		zxvision_print_string_defaults_fillspc(menu_debug_dma_tsconf_zxuno_overlay_window,1,linea++,texto_dma);

		//WR4. Bits D6 D5:
		//#       0   0 = Byte mode -> Do not use (Behaves like Continuous mode, Byte mode on Z80 DMA)
		//#       0   1 = Continuous mode
		//#       1   0 = Burst mode
		//#       1   1 = Do not use

		z80_byte modo_transferencia=(datagear_wr4>>5)&3;
		if (modo_transferencia==0) 		sprintf (texto_dma,"Mode: Byte mode");
		else if (modo_transferencia==1) sprintf (texto_dma,"Mode: Continuous");
		else if (modo_transferencia==2) sprintf (texto_dma,"Mode: Burst");
		else 							sprintf (texto_dma,"Mode: Do not use");

		//menu_escribe_linea_opcion(linea++,-1,1,texto_dma);
		zxvision_print_string_defaults_fillspc(menu_debug_dma_tsconf_zxuno_overlay_window,1,linea++,texto_dma);

        zxvision_print_string_defaults_fillspc_format(menu_debug_dma_tsconf_zxuno_overlay_window,1,linea++,
            "Current Transfer: %s",(datagear_is_dma_transfering.v ? "Yes" : "No"));

	}

	else {

        if (MACHINE_IS_TSCONF) {
            menu_debug_dma_existe_dma=1;

            //Construimos 16 valores posibles segun rw (bit bajo) y ddev (bits altos)
            int dma_type=debug_tsconf_dma_ddev*2+debug_tsconf_dma_rw;
                            //18 maximo el tipo
                            //  012345678901234567890123
                            //24. mas dos de margen banda y banda: 26
            sprintf (texto_dma,"Type: %s",tsconf_dma_types[dma_type]);
            //menu_escribe_linea_opcion(linea++,-1,1,texto_dma);
            zxvision_print_string_defaults_fillspc(menu_debug_dma_tsconf_zxuno_overlay_window,1,linea++,texto_dma);

            sprintf (texto_dma,"Source:      %06XH",debug_tsconf_dma_source);
            //menu_escribe_linea_opcion(linea++,-1,1,texto_dma);
            zxvision_print_string_defaults_fillspc(menu_debug_dma_tsconf_zxuno_overlay_window,1,linea++,texto_dma);

            sprintf (texto_dma,"Destination: %06XH",debug_tsconf_dma_destination);
            //menu_escribe_linea_opcion(linea++,-1,1,texto_dma);
            zxvision_print_string_defaults_fillspc(menu_debug_dma_tsconf_zxuno_overlay_window,1,linea++,texto_dma);

            sprintf (texto_dma,"Burst length: %3d",debug_tsconf_dma_burst_length);
            //menu_escribe_linea_opcion(linea++,-1,1,texto_dma);
            zxvision_print_string_defaults_fillspc(menu_debug_dma_tsconf_zxuno_overlay_window,1,linea++,texto_dma);

            sprintf (texto_dma,"Burst number: %3d",debug_tsconf_dma_burst_number);
            //menu_escribe_linea_opcion(linea++,-1,1,texto_dma);
            zxvision_print_string_defaults_fillspc(menu_debug_dma_tsconf_zxuno_overlay_window,1,linea++,texto_dma);

                            //Maximo 25
            sprintf (texto_dma,"Align: %s %s",(debug_tsconf_dma_s_align ? "Source" : "      "),(debug_tsconf_dma_d_align ? "Destination" : "") );
            //menu_escribe_linea_opcion(linea++,-1,1,texto_dma);
            zxvision_print_string_defaults_fillspc(menu_debug_dma_tsconf_zxuno_overlay_window,1,linea++,texto_dma);

            sprintf (texto_dma,"Align size: %d",(debug_tsconf_dma_addr_align_size+1)*256);
            //menu_escribe_linea_opcion(linea++,-1,1,texto_dma);
            zxvision_print_string_defaults_fillspc(menu_debug_dma_tsconf_zxuno_overlay_window,1,linea++,texto_dma);

        }

        else if (MACHINE_IS_ZXUNO) {
            menu_debug_dma_existe_dma=1;

            z80_byte dma_ctrl=zxuno_ports[0xa0];
            z80_byte dma_type=(dma_ctrl & (4+8))>>2;
            z80_byte dma_mode=dma_ctrl & 3;

            z80_int dma_src=value_8_to_16(zxuno_dmareg[0][1],zxuno_dmareg[0][0]);
            z80_int dma_dst=value_8_to_16(zxuno_dmareg[1][1],zxuno_dmareg[1][0]);
            z80_int dma_pre=value_8_to_16(zxuno_dmareg[2][1],zxuno_dmareg[2][0]);
            z80_int dma_len=value_8_to_16(zxuno_dmareg[3][1],zxuno_dmareg[3][0]);
            z80_int dma_prob=value_8_to_16(zxuno_dmareg[4][1],zxuno_dmareg[4][0]);
            z80_byte dma_stat=zxuno_ports[0xa6];

            sprintf (texto_dma,"Type: %s",zxuno_dma_types[dma_type]);
            //menu_escribe_linea_opcion(linea++,-1,1,texto_dma);
            zxvision_print_string_defaults_fillspc(menu_debug_dma_tsconf_zxuno_overlay_window,1,linea++,texto_dma);

            sprintf (texto_dma,"Mode: %s",zxuno_dma_modes[dma_mode]);
            //menu_escribe_linea_opcion(linea++,-1,1,texto_dma);
            zxvision_print_string_defaults_fillspc(menu_debug_dma_tsconf_zxuno_overlay_window,1,linea++,texto_dma);

            sprintf (texto_dma,"Source:      %04XH",dma_src);
            //menu_escribe_linea_opcion(linea++,-1,1,texto_dma);
            zxvision_print_string_defaults_fillspc(menu_debug_dma_tsconf_zxuno_overlay_window,1,linea++,texto_dma);

            sprintf (texto_dma,"Destination: %04XH",dma_dst);
            //menu_escribe_linea_opcion(linea++,-1,1,texto_dma);
            zxvision_print_string_defaults_fillspc(menu_debug_dma_tsconf_zxuno_overlay_window,1,linea++,texto_dma);

            sprintf (texto_dma,"Length:      %5d",dma_len);
            //menu_escribe_linea_opcion(linea++,-1,1,texto_dma);
            zxvision_print_string_defaults_fillspc(menu_debug_dma_tsconf_zxuno_overlay_window,1,linea++,texto_dma);

            sprintf (texto_dma,"Preescaler:  %5d",dma_pre);
            //menu_escribe_linea_opcion(linea++,-1,1,texto_dma);
            zxvision_print_string_defaults_fillspc(menu_debug_dma_tsconf_zxuno_overlay_window,1,linea++,texto_dma);

            char prob_type[10];
            if (dma_ctrl&16) strcpy(prob_type,"dst");
            else strcpy(prob_type,"src");

            sprintf (texto_dma,"Prob: (%s)  %04XH",prob_type,dma_prob);
            //menu_escribe_linea_opcion(linea++,-1,1,texto_dma);
            zxvision_print_string_defaults_fillspc(menu_debug_dma_tsconf_zxuno_overlay_window,1,linea++,texto_dma);

            sprintf (texto_dma,"Stat:          %02XH",dma_stat);
            //menu_escribe_linea_opcion(linea++,-1,1,texto_dma);
            zxvision_print_string_defaults_fillspc(menu_debug_dma_tsconf_zxuno_overlay_window,1,linea++,texto_dma);

        }

        else {
            //Maquina sin DMA
        }

	}

    if (menu_debug_dma_existe_dma) {
        zxvision_print_string_defaults_fillspc_format(menu_debug_dma_tsconf_zxuno_overlay_window,1,0,"Type: %s",nombre_dma);
    }


}






void menu_debug_dma_tsconf_zxuno_disable(MENU_ITEM_PARAMETERS)
{
	if (datagear_dma_emulation.v) datagear_dma_is_disabled.v ^=1;

	else {
		if (MACHINE_IS_TSCONF) tsconf_dma_disabled.v ^=1;
		if (MACHINE_IS_ZXUNO) zxuno_dma_disabled.v ^=1;
	}
}



zxvision_window *menu_debug_dma_window;


void menu_debug_dma_overlay(void)
{

    menu_speech_set_tecla_pulsada(); //Si no, envia continuamente todo ese texto a speech

    //si ventana minimizada, no ejecutar todo el codigo de overlay
    if (menu_debug_dma_window->is_minimized) return;



    menu_debug_dma_draw(menu_debug_dma_window);


    //Mostrar contenido
    zxvision_draw_window_contents(menu_debug_dma_window);

}




//Almacenar la estructura de ventana aqui para que se pueda referenciar desde otros sitios
zxvision_window zxvision_window_debug_dma;


void menu_debug_dma(MENU_ITEM_PARAMETERS)
{
	menu_espera_no_tecla();

    if (!menu_multitarea) {
        menu_warn_message("This window needs multitask enabled");
        return;
    }

    zxvision_window *ventana;
    ventana=&zxvision_window_debug_dma;

	//IMPORTANTE! no crear ventana si ya existe. Esto hay que hacerlo en todas las ventanas que permiten background.
	//si no se hiciera, se crearia la misma ventana, y en la lista de ventanas activas , al redibujarse,
	//la primera ventana repetida apuntaria a la segunda, que es el mismo puntero, y redibujaria la misma, y se quedaria en bucle colgado
	//zxvision_delete_window_if_exists(ventana);

    //Crear ventana si no existe
    if (!zxvision_if_window_already_exists(ventana)) {
        int xventana,yventana,ancho_ventana,alto_ventana,is_minimized,is_maximized,ancho_antes_minimize,alto_antes_minimize;

        if (!util_find_window_geometry("debugdma",&xventana,&yventana,&ancho_ventana,&alto_ventana,&is_minimized,&is_maximized,&ancho_antes_minimize,&alto_antes_minimize)) {
            ancho_ventana=27;
            alto_ventana=14;

            xventana=menu_center_x()-ancho_ventana/2;
            yventana=menu_center_y()-alto_ventana/2;
        }


        zxvision_new_window_gn_cim(ventana,xventana,yventana,ancho_ventana,alto_ventana,ancho_ventana-1,alto_ventana-2,"Debug DMA",
            "debugdma",is_minimized,is_maximized,ancho_antes_minimize,alto_antes_minimize);

        ventana->can_be_backgrounded=1;

    }

    //Si ya existe, activar esta ventana
    else {
        zxvision_activate_this_window(ventana);
    }



	zxvision_draw_window(ventana);


    menu_debug_dma_window=ventana; //Decimos que el overlay lo hace sobre la ventana que tenemos aqui


    //cambio overlay
    zxvision_set_window_overlay(ventana,menu_debug_dma_overlay);


    //Toda ventana que este listada en zxvision_known_window_names_array debe permitir poder salir desde aqui
    //Se sale despues de haber inicializado overlay y de cualquier otra variable que necesite el overlay
    if (zxvision_currently_restoring_windows_on_start) {
        //printf ("Saliendo de ventana ya que la estamos restaurando en startup\n");
        return;
    }



	menu_item *array_menu_debug_dma_tsconf_zxuno;
    menu_item item_seleccionado;
    int retorno_menu;
    int opcion_seleccionada=0;
    do {


        int lin=9;



        int condicion_dma_disabled=tsconf_dma_disabled.v;


        if (MACHINE_IS_ZXUNO) {
            lin++;
            condicion_dma_disabled=zxuno_dma_disabled.v;
        }

        if (datagear_dma_emulation.v) {
            lin++;
            condicion_dma_disabled=datagear_dma_is_disabled.v;
        }

        if (MACHINE_IS_TSCONF) {
        }

        if (menu_debug_dma_existe_dma) {
            menu_add_item_menu_inicial_format(&array_menu_debug_dma_tsconf_zxuno,MENU_OPCION_NORMAL,menu_debug_dma_tsconf_zxuno_disable,NULL,"~~DMA: %s",
                (condicion_dma_disabled ? "Disabled" : "Enabled ") );  //Enabled acaba con espacio para borrar rastro de texto "Disabled"
            menu_add_item_menu_shortcut(array_menu_debug_dma_tsconf_zxuno,'d');
            menu_add_item_menu_ayuda(array_menu_debug_dma_tsconf_zxuno,"Disable DMA");
            menu_add_item_menu_tabulado(array_menu_debug_dma_tsconf_zxuno,1,lin);
        }
        else {
            //Borrar cualquier resto de info de maquina anterior con DMA
            zxvision_cls(ventana);
            menu_add_item_menu_inicial_format(&array_menu_debug_dma_tsconf_zxuno,MENU_OPCION_NORMAL,NULL,NULL,"No DMA enabled");
            menu_add_item_menu_tabulado(array_menu_debug_dma_tsconf_zxuno,1,0);
        }



		//Nombre de ventana solo aparece en el caso de stdout
        retorno_menu=menu_dibuja_menu_no_title_lang(&opcion_seleccionada,&item_seleccionado,array_menu_debug_dma_tsconf_zxuno,"Debug DMA" );


	    if (retorno_menu!=MENU_RETORNO_BACKGROUND) {

            if ((item_seleccionado.tipo_opcion&MENU_OPCION_ESC)==0 && retorno_menu>=0) {
                //llamamos por valor de funcion
                if (item_seleccionado.menu_funcion!=NULL) {
                    //printf ("actuamos por funcion\n");
                    item_seleccionado.menu_funcion(item_seleccionado.valor_opcion);
                }
            }
        }

    } while ( (item_seleccionado.tipo_opcion&MENU_OPCION_ESC)==0 && retorno_menu!=MENU_RETORNO_ESC && !salir_todos_menus && retorno_menu!=MENU_RETORNO_BACKGROUND);


    //En caso de menus tabulados, suele ser necesario esto. Si no, la ventana se quedaria visible
	util_add_window_geometry_compact(ventana);


    if (retorno_menu==MENU_RETORNO_BACKGROUND) {
        zxvision_message_put_window_background();
    }

    else {
        //En caso de menus tabulados, es responsabilidad de este de liberar ventana
        zxvision_destroy_window(ventana);
    }


}






int menu_tsconf_layer_valor_contador_segundo_anterior;

char *menu_tsconf_layer_aux_usedunused_used="In use";
char *menu_tsconf_layer_aux_usedunused_unused="Unused";

char *menu_tsconf_layer_aux_usedunused(int value)
{
	if (value) return menu_tsconf_layer_aux_usedunused_used;
	else return menu_tsconf_layer_aux_usedunused_unused;
}


//zxvision_window *menu_tsconf_layer_overlay_window;

void menu_tsconf_layer_overlay_mostrar_texto(zxvision_window *w)
{

    zxvision_window *menu_tsconf_layer_overlay_window=w;

 int linea;

    linea=0;


        //mostrarlos siempre a cada refresco

                char texto_layer[33];

				if (MACHINE_IS_TSCONF) {

					//menu_escribe_linea_opcion(linea,-1,1,"Border: ");
					zxvision_print_string_defaults_fillspc(menu_tsconf_layer_overlay_window,1,linea,"Border: ");
					linea +=3;

					sprintf (texto_layer,"ULA:       %s",menu_tsconf_layer_aux_usedunused(tsconf_if_ula_enabled()));
					//menu_escribe_linea_opcion(linea,-1,1,texto_layer);
					zxvision_print_string_defaults_fillspc(menu_tsconf_layer_overlay_window,1,linea,texto_layer);
					linea +=3;

					sprintf (texto_layer,"Sprites 0: %s",menu_tsconf_layer_aux_usedunused(tsconf_if_sprites_enabled()));
					//menu_escribe_linea_opcion(linea,-1,1,texto_layer);
					zxvision_print_string_defaults_fillspc(menu_tsconf_layer_overlay_window,1,linea,texto_layer);
					linea +=3;

					sprintf (texto_layer,"Tiles 0:   %s",menu_tsconf_layer_aux_usedunused(tsconf_if_tiles_zero_enabled()));
					//menu_escribe_linea_opcion(linea,-1,1,texto_layer);
					zxvision_print_string_defaults_fillspc(menu_tsconf_layer_overlay_window,1,linea,texto_layer);
					linea +=3;

					sprintf (texto_layer,"Sprites 1: %s",menu_tsconf_layer_aux_usedunused(tsconf_if_sprites_enabled()));
					//menu_escribe_linea_opcion(linea,-1,1,texto_layer);
					zxvision_print_string_defaults_fillspc(menu_tsconf_layer_overlay_window,1,linea,texto_layer);
					linea +=3;

					sprintf (texto_layer,"Tiles 1:   %s",menu_tsconf_layer_aux_usedunused(tsconf_if_tiles_one_enabled()));
					//menu_escribe_linea_opcion(linea,-1,1,texto_layer);
					zxvision_print_string_defaults_fillspc(menu_tsconf_layer_overlay_window,1,linea,texto_layer);
					linea +=3;

					sprintf (texto_layer,"Sprites 2: %s",menu_tsconf_layer_aux_usedunused(tsconf_if_sprites_enabled()));
					//menu_escribe_linea_opcion(linea,-1,1,texto_layer);
					zxvision_print_string_defaults_fillspc(menu_tsconf_layer_overlay_window,1,linea,texto_layer);
					linea +=3;
				}

				else if (MACHINE_IS_TBBLUE) {
	                sprintf (texto_layer,"ULA:       %s",menu_tsconf_layer_aux_usedunused(tbblue_if_ula_is_enabled()) );
    	            //menu_escribe_linea_opcion(linea,-1,1,texto_layer);
					zxvision_print_string_defaults_fillspc(menu_tsconf_layer_overlay_window,1,linea,texto_layer);
					linea +=3;

	                sprintf (texto_layer,"Tiles:     %s",menu_tsconf_layer_aux_usedunused(tbblue_if_tilemap_enabled()) );
    	            //menu_escribe_linea_opcion(linea,-1,1,texto_layer);
					zxvision_print_string_defaults_fillspc(menu_tsconf_layer_overlay_window,1,linea,texto_layer);
					linea +=3;

                	sprintf (texto_layer,"Sprites:   %s",menu_tsconf_layer_aux_usedunused(tbblue_if_sprites_enabled() ));
                	//menu_escribe_linea_opcion(linea,-1,1,texto_layer);
					zxvision_print_string_defaults_fillspc(menu_tsconf_layer_overlay_window,1,linea,texto_layer);
					linea +=3;

					sprintf (texto_layer,"Layer 2:   %s",menu_tsconf_layer_aux_usedunused(tbblue_is_active_layer2() ) );
    	            //menu_escribe_linea_opcion(linea,-1,1,texto_layer);
					zxvision_print_string_defaults_fillspc(menu_tsconf_layer_overlay_window,1,linea,texto_layer);
					linea +=3;

                    //Hueco para L2 allow priority bit
                    linea +=2;

					//Layer priorities

					z80_byte prio=tbblue_get_layers_priorities();
					sprintf (texto_layer,"Priorities: (%d)",prio);
					//menu_escribe_linea_opcion(linea++,-1,1,texto_layer);
					zxvision_print_string_defaults_fillspc(menu_tsconf_layer_overlay_window,1,linea++,texto_layer);


					int i;
					for (i=0;i<4;i++) {
						char nombre_capa[32];
						strcpy(nombre_capa,tbblue_get_string_layer_prio(i,prio) );
						//if (strcmp(nombre_capa,"ULA&Tiles")) strcpy(nombre_capa,"  ULA  "); //meter espacios para centrarlo
						//las otras capas son "Sprites" y "Layer 2" y ocupan lo mismo

													//     Sprites
													//    ULA&Tiles
                                                    //   ULA+Tiles+Layer2 Blend-5
						if (i!=3) strcpy (texto_layer,"|--------------------------|");
						else      strcpy (texto_layer,"v--------------------------v");

						//Centrar el nombre de capa
						int longitud_medio=strlen(nombre_capa)/2;
						int medio=strlen(texto_layer)/2;
						int pos=medio-longitud_medio;
						if (pos<0) pos=0;

						//Meter texto centrado y quitar 0 del final
						strcpy(&texto_layer[pos],nombre_capa);

						int final=strlen(texto_layer);
						texto_layer[final]='-';

						//menu_escribe_linea_opcion(linea++,-1,1,texto_layer);
						zxvision_print_string_defaults_fillspc(menu_tsconf_layer_overlay_window,1,linea++,texto_layer);

					}
				}

                else if (MACHINE_HAS_VDP_9918A) {

                    //if (!vdp_9918a_si_sms_video_mode4()) {
                    if (!MACHINE_IS_SMS) {

                        //menu_escribe_linea_opcion(linea,-1,1,"Border: ");
                        zxvision_print_string_defaults_fillspc(menu_tsconf_layer_overlay_window,1,linea,"Border: ");
                        linea +=3;

                        zxvision_print_string_defaults_fillspc(menu_tsconf_layer_overlay_window,1,linea,"Pixels:");
                        linea +=3;

                        zxvision_print_string_defaults_fillspc(menu_tsconf_layer_overlay_window,1,linea,"Sprites:");
                        linea +=3;

                    }

                    else {
                        //menu_escribe_linea_opcion(linea,-1,1,"Border: ");
                        zxvision_print_string_defaults_fillspc(menu_tsconf_layer_overlay_window,1,linea,"Border: ");
                        linea +=3;

                        zxvision_print_string_defaults_fillspc(menu_tsconf_layer_overlay_window,1,linea,"Tiles Background:");
                        linea +=3;


                            zxvision_print_string_defaults_fillspc(menu_tsconf_layer_overlay_window,1,linea,"Tiles Foreground:");
                            linea +=3;



                        zxvision_print_string_defaults_fillspc(menu_tsconf_layer_overlay_window,1,linea,"Sprites:");
                        linea +=3;


                        linea++;
                        zxvision_print_string_defaults(menu_tsconf_layer_overlay_window,1,linea,"Force show column 0:");
                        linea +=2;

                        zxvision_print_string_defaults(menu_tsconf_layer_overlay_window,1,linea,"Lock scroll horiz.: ");
                        linea +=2;

                        zxvision_print_string_defaults(menu_tsconf_layer_overlay_window,1,linea,"Lock scroll vert.:  ");
                        linea +=2;


                    }

                }

                else {
                    //Maquina sin capas
                }



}




void menu_tsconf_layer_settings_ula(MENU_ITEM_PARAMETERS)
{
	tsconf_force_disable_layer_ula.v ^=1;
}


void menu_tsconf_layer_settings_sprites_zero(MENU_ITEM_PARAMETERS)
{
	tsconf_force_disable_layer_sprites_zero.v ^=1;
}

void menu_tsconf_layer_settings_sprites_one(MENU_ITEM_PARAMETERS)
{
	tsconf_force_disable_layer_sprites_one.v ^=1;
}

void menu_tsconf_layer_settings_sprites_two(MENU_ITEM_PARAMETERS)
{
	tsconf_force_disable_layer_sprites_two.v ^=1;
}

void menu_tsconf_layer_settings_tiles_zero(MENU_ITEM_PARAMETERS)
{
	tsconf_force_disable_layer_tiles_zero.v ^=1;
}

void menu_tsconf_layer_settings_tiles_one(MENU_ITEM_PARAMETERS)
{
	tsconf_force_disable_layer_tiles_one.v ^=1;
}

void menu_tsconf_layer_settings_border(MENU_ITEM_PARAMETERS)
{
	tsconf_force_disable_layer_border.v ^=1;
}

/*
extern z80_bit tbblue_force_disable_layer_ula;
extern z80_bit tbblue_force_disable_layer_sprites;
extern z80_bit tbblue_force_disable_layer_layer_two;
*/

void menu_tbblue_layer_settings_sprites(MENU_ITEM_PARAMETERS)
{
	tbblue_force_disable_layer_sprites.v ^=1;
}

void menu_tbblue_layer_settings_ula(MENU_ITEM_PARAMETERS)
{
	tbblue_force_disable_layer_ula.v ^=1;
}

void menu_tbblue_layer_settings_tilemap(MENU_ITEM_PARAMETERS)
{
	tbblue_force_disable_layer_tilemap.v ^=1;
}

void menu_tbblue_layer_settings_layer_two(MENU_ITEM_PARAMETERS)
{
	tbblue_force_disable_layer_layer_two.v ^=1;
}

void menu_tbblue_layer_reveal_ula(MENU_ITEM_PARAMETERS)
{
	tbblue_reveal_layer_ula.v ^=1;
}

void menu_tbblue_layer_reveal_tiles(MENU_ITEM_PARAMETERS)
{
	tbblue_reveal_layer_tiles.v ^=1;
}

void menu_tbblue_layer_reveal_layer2(MENU_ITEM_PARAMETERS)
{
	tbblue_reveal_layer_layer2.v ^=1;
}

void menu_tbblue_layer_reveal_sprites(MENU_ITEM_PARAMETERS)
{
	tbblue_reveal_layer_sprites.v ^=1;
}



void menu_tsconf_layer_reveal_ula(MENU_ITEM_PARAMETERS)
{
	tsconf_reveal_layer_ula.v ^=1;
}

void menu_tsconf_layer_reveal_sprites_zero(MENU_ITEM_PARAMETERS)
{
	tsconf_reveal_layer_sprites_zero.v ^=1;
}

void menu_tsconf_layer_reveal_sprites_one(MENU_ITEM_PARAMETERS)
{
	tsconf_reveal_layer_sprites_one.v ^=1;
}


void menu_tsconf_layer_reveal_sprites_two(MENU_ITEM_PARAMETERS)
{
	tsconf_reveal_layer_sprites_two.v ^=1;
}


void menu_tsconf_layer_reveal_tiles_zero(MENU_ITEM_PARAMETERS)
{
	tsconf_reveal_layer_tiles_zero.v ^=1;
}

void menu_tsconf_layer_reveal_tiles_one(MENU_ITEM_PARAMETERS)
{
	tsconf_reveal_layer_tiles_one.v ^=1;
}



void menu_msx_layer_settings_border(MENU_ITEM_PARAMETERS)
{
	vdp_9918a_force_disable_layer_border.v ^=1;
}


void menu_msx_layer_settings_ula(MENU_ITEM_PARAMETERS)
{
	vdp_9918a_force_disable_layer_ula.v ^=1;
}

void menu_msx_layer_settings_sprites(MENU_ITEM_PARAMETERS)
{
	vdp_9918a_force_disable_layer_sprites.v ^=1;
}




void menu_msx_layer_reveal_ula(MENU_ITEM_PARAMETERS)
{
	vdp_9918a_reveal_layer_ula.v ^=1;
}

void menu_msx_layer_reveal_sprites(MENU_ITEM_PARAMETERS)
{
	vdp_9918a_reveal_layer_sprites.v ^=1;
}

void menu_sms_layer_settings_force_col0(MENU_ITEM_PARAMETERS)
{
    vdp_9918a_sms_force_show_column_zero.v ^=1;
}

void menu_sms_layer_settings_lock_scroll_horiz(MENU_ITEM_PARAMETERS)
{
    vdp_9918a_sms_lock_scroll_horizontal.v ^=1;
}

void menu_sms_layer_settings_lock_scroll_vert(MENU_ITEM_PARAMETERS)
{
    vdp_9918a_sms_lock_scroll_vertical.v ^=1;
}


void menu_sms_layer_settings_tile_fg(MENU_ITEM_PARAMETERS)
{
    vdp_9918a_force_disable_layer_tile_fg.v ^=1;
}

void menu_sms_layer_reveal_tile_fg(MENU_ITEM_PARAMETERS)
{
    vdp_9918a_reveal_layer_tile_fg.v ^=1;
}

void menu_sms_layer_settings_tile_bg(MENU_ITEM_PARAMETERS)
{
    vdp_9918a_force_disable_layer_tile_bg.v ^=1;

    //Para que tambien funcione en casos de SMS con modos no 4, en realvideo
    vdp_9918a_force_disable_layer_ula.v=vdp_9918a_force_disable_layer_tile_bg.v;
}

void menu_sms_layer_reveal_tile_bg(MENU_ITEM_PARAMETERS)
{
    vdp_9918a_reveal_layer_tile_bg.v ^=1;

    //Para que tambien funcione en casos de SMS con modos no 4, en realvideo
    vdp_9918a_reveal_layer_ula.v=vdp_9918a_reveal_layer_tile_bg.v;
}

void menu_sms_layer_force_bg_tiles(MENU_ITEM_PARAMETERS)
{
    vdp_9918a_force_bg_tiles.v ^=1;
}

void menu_tbblue_layer_settings_layer2_priority_bit(MENU_ITEM_PARAMETERS)
{
    tbblue_allow_layer2_priority_bit.v ^=1;
}


zxvision_window *menu_video_layers_window;


void menu_video_layers_overlay(void)
{

    menu_speech_set_tecla_pulsada(); //Si no, envia continuamente todo ese texto a speech

    //si ventana minimizada, no ejecutar todo el codigo de overlay
    if (menu_video_layers_window->is_minimized) return;


 	menu_speech_set_tecla_pulsada(); //Si no, envia continuamente todo ese texto a speech


    //esto hara ejecutar esto 2 veces por segundo
    if ( ((contador_segundo%500) == 0 && menu_tsconf_layer_valor_contador_segundo_anterior!=contador_segundo) || menu_multitarea==0) {

        menu_tsconf_layer_valor_contador_segundo_anterior=contador_segundo;
        //printf ("Refrescando. contador_segundo=%d\n",contador_segundo);


		menu_tsconf_layer_overlay_mostrar_texto(menu_video_layers_window);


    }


    //Mostrar contenido
    zxvision_draw_window_contents(menu_video_layers_window);

}




//Almacenar la estructura de ventana aqui para que se pueda referenciar desde otros sitios
zxvision_window zxvision_window_video_layers;


void menu_video_layers(MENU_ITEM_PARAMETERS)
{
	menu_espera_no_tecla();

    if (!menu_multitarea) {
        menu_warn_message("This window needs multitask enabled");
        return;
    }

    zxvision_window *ventana;
    ventana=&zxvision_window_video_layers;

	//IMPORTANTE! no crear ventana si ya existe. Esto hay que hacerlo en todas las ventanas que permiten background.
	//si no se hiciera, se crearia la misma ventana, y en la lista de ventanas activas , al redibujarse,
	//la primera ventana repetida apuntaria a la segunda, que es el mismo puntero, y redibujaria la misma, y se quedaria en bucle colgado
	//zxvision_delete_window_if_exists(ventana);

    //Crear ventana si no existe
    if (!zxvision_if_window_already_exists(ventana)) {
        int xventana,yventana,ancho_ventana,alto_ventana,is_minimized,is_maximized,ancho_antes_minimize,alto_antes_minimize;

        if (!util_find_window_geometry("videolayers",&xventana,&yventana,&ancho_ventana,&alto_ventana,&is_minimized,&is_maximized,&ancho_antes_minimize,&alto_antes_minimize)) {
            ancho_ventana=21;
            alto_ventana=22;

            if (MACHINE_IS_TBBLUE) {
                alto_ventana=22;
                ancho_ventana=30;
            }


            else if (MACHINE_HAS_VDP_9918A) {
                alto_ventana=11;

                if (MACHINE_IS_SMS) {
                    //Para que quepa el show column 0, etc
                    alto_ventana=21;
                    ancho_ventana=32;
                }
            }


            xventana=menu_center_x()-ancho_ventana/2;
            yventana=menu_center_y()-alto_ventana/2;
        }


        zxvision_new_window_gn_cim(ventana,xventana,yventana,ancho_ventana,alto_ventana,ancho_ventana-1,alto_ventana-2,"Video Layers",
            "videolayers",is_minimized,is_maximized,ancho_antes_minimize,alto_antes_minimize);

        ventana->can_be_backgrounded=1;

    }

    //Si ya existe, activar esta ventana
    else {
        zxvision_activate_this_window(ventana);
    }

	zxvision_draw_window(ventana);


    menu_video_layers_window=ventana; //Decimos que el overlay lo hace sobre la ventana que tenemos aqui


    //cambio overlay
    zxvision_set_window_overlay(ventana,menu_video_layers_overlay);


    //Toda ventana que este listada en zxvision_known_window_names_array debe permitir poder salir desde aqui
    //Se sale despues de haber inicializado overlay y de cualquier otra variable que necesite el overlay
    if (zxvision_currently_restoring_windows_on_start) {
            //printf ("Saliendo de ventana ya que la estamos restaurando en startup\n");
            return;
    }

    //Borrar contenido por si se cambia de una maquina a otra
    zxvision_cls(ventana);

    menu_item *array_menu_tsconf_layer_settings;
    menu_item item_seleccionado;
    int retorno_menu;

    do {

        int lin=1;

		if (MACHINE_IS_TSCONF) {

 			menu_add_item_menu_inicial_format(&array_menu_tsconf_layer_settings,MENU_OPCION_NORMAL,menu_tsconf_layer_settings_border,NULL,"%s",(tsconf_force_disable_layer_border.v ? "Disabled" : "Enabled "));
			menu_add_item_menu_tabulado(array_menu_tsconf_layer_settings,1,lin);
			lin+=3;

			menu_add_item_menu_format(array_menu_tsconf_layer_settings,MENU_OPCION_NORMAL,menu_tsconf_layer_settings_ula,NULL,"%s",(tsconf_force_disable_layer_ula.v ? "Disabled" : "Enabled "));
			menu_add_item_menu_tabulado(array_menu_tsconf_layer_settings,1,lin);
			menu_add_item_menu_format(array_menu_tsconf_layer_settings,MENU_OPCION_NORMAL,menu_tsconf_layer_reveal_ula,NULL,"%s",(tsconf_reveal_layer_ula.v ? "Reveal" : "Normal"));
			menu_add_item_menu_tabulado(array_menu_tsconf_layer_settings,12,lin);
			lin+=3;

			menu_add_item_menu_format(array_menu_tsconf_layer_settings,MENU_OPCION_NORMAL,menu_tsconf_layer_settings_sprites_zero,NULL,"%s",(tsconf_force_disable_layer_sprites_zero.v ? "Disabled" : "Enabled "));
			menu_add_item_menu_tabulado(array_menu_tsconf_layer_settings,1,lin);
			menu_add_item_menu_format(array_menu_tsconf_layer_settings,MENU_OPCION_NORMAL,menu_tsconf_layer_reveal_sprites_zero,NULL,"%s",(tsconf_reveal_layer_sprites_zero.v ? "Reveal" : "Normal"));
			menu_add_item_menu_tabulado(array_menu_tsconf_layer_settings,12,lin);
			lin+=3;

			menu_add_item_menu_format(array_menu_tsconf_layer_settings,MENU_OPCION_NORMAL,menu_tsconf_layer_settings_tiles_zero,NULL,"%s",(tsconf_force_disable_layer_tiles_zero.v ? "Disabled" : "Enabled "));
			menu_add_item_menu_tabulado(array_menu_tsconf_layer_settings,1,lin);
			menu_add_item_menu_format(array_menu_tsconf_layer_settings,MENU_OPCION_NORMAL,menu_tsconf_layer_reveal_tiles_zero,NULL,"%s",(tsconf_reveal_layer_tiles_zero.v ? "Reveal" : "Normal"));
			menu_add_item_menu_tabulado(array_menu_tsconf_layer_settings,12,lin);
			lin+=3;

			menu_add_item_menu_format(array_menu_tsconf_layer_settings,MENU_OPCION_NORMAL,menu_tsconf_layer_settings_sprites_one,NULL,"%s",(tsconf_force_disable_layer_sprites_one.v ? "Disabled" : "Enabled "));
			menu_add_item_menu_tabulado(array_menu_tsconf_layer_settings,1,lin);
			menu_add_item_menu_format(array_menu_tsconf_layer_settings,MENU_OPCION_NORMAL,menu_tsconf_layer_reveal_sprites_one,NULL,"%s",(tsconf_reveal_layer_sprites_one.v ? "Reveal" : "Normal"));
			menu_add_item_menu_tabulado(array_menu_tsconf_layer_settings,12,lin);
			lin+=3;

			menu_add_item_menu_format(array_menu_tsconf_layer_settings,MENU_OPCION_NORMAL,menu_tsconf_layer_settings_tiles_one,NULL,"%s",(tsconf_force_disable_layer_tiles_one.v ? "Disabled" : "Enabled "));
			menu_add_item_menu_tabulado(array_menu_tsconf_layer_settings,1,lin);
			menu_add_item_menu_format(array_menu_tsconf_layer_settings,MENU_OPCION_NORMAL,menu_tsconf_layer_reveal_tiles_one,NULL,"%s",(tsconf_reveal_layer_tiles_one.v ? "Reveal" : "Normal"));
			menu_add_item_menu_tabulado(array_menu_tsconf_layer_settings,12,lin);
			lin+=3;

			menu_add_item_menu_format(array_menu_tsconf_layer_settings,MENU_OPCION_NORMAL,menu_tsconf_layer_settings_sprites_two,NULL,"%s",(tsconf_force_disable_layer_sprites_two.v ? "Disabled" : "Enabled "));
			menu_add_item_menu_tabulado(array_menu_tsconf_layer_settings,1,lin);
			menu_add_item_menu_format(array_menu_tsconf_layer_settings,MENU_OPCION_NORMAL,menu_tsconf_layer_reveal_sprites_two,NULL,"%s",(tsconf_reveal_layer_sprites_two.v ? "Reveal" : "Normal"));
			menu_add_item_menu_tabulado(array_menu_tsconf_layer_settings,12,lin);
			lin+=3;

		}

		else if (MACHINE_IS_TBBLUE) {
 			menu_add_item_menu_inicial_format(&array_menu_tsconf_layer_settings,MENU_OPCION_NORMAL,menu_tbblue_layer_settings_ula,NULL,"%s",(tbblue_force_disable_layer_ula.v ? "Disabled" : "Enabled "));
			menu_add_item_menu_tabulado(array_menu_tsconf_layer_settings,1,lin);
 			menu_add_item_menu_format(array_menu_tsconf_layer_settings,MENU_OPCION_NORMAL,menu_tbblue_layer_reveal_ula,NULL,"%s",(tbblue_reveal_layer_ula.v ? "Reveal" : "Normal"));
			menu_add_item_menu_tabulado(array_menu_tsconf_layer_settings,12,lin);
			lin+=3;

 			menu_add_item_menu_format(array_menu_tsconf_layer_settings,MENU_OPCION_NORMAL,menu_tbblue_layer_settings_tilemap,NULL,"%s",(tbblue_force_disable_layer_tilemap.v ? "Disabled" : "Enabled "));
			menu_add_item_menu_tabulado(array_menu_tsconf_layer_settings,1,lin);
 			menu_add_item_menu_format(array_menu_tsconf_layer_settings,MENU_OPCION_NORMAL,menu_tbblue_layer_reveal_tiles,NULL,"%s",(tbblue_reveal_layer_tiles.v ? "Reveal" : "Normal"));
			menu_add_item_menu_tabulado(array_menu_tsconf_layer_settings,12,lin);
			lin+=3;

			menu_add_item_menu_format(array_menu_tsconf_layer_settings,MENU_OPCION_NORMAL,menu_tbblue_layer_settings_sprites,NULL,"%s",(tbblue_force_disable_layer_sprites.v ? "Disabled" : "Enabled "));
			menu_add_item_menu_tabulado(array_menu_tsconf_layer_settings,1,lin);
 			menu_add_item_menu_format(array_menu_tsconf_layer_settings,MENU_OPCION_NORMAL,menu_tbblue_layer_reveal_sprites,NULL,"%s",(tbblue_reveal_layer_sprites.v ? "Reveal" : "Normal"));
			menu_add_item_menu_tabulado(array_menu_tsconf_layer_settings,12,lin);
			lin+=3;

			menu_add_item_menu_format(array_menu_tsconf_layer_settings,MENU_OPCION_NORMAL,menu_tbblue_layer_settings_layer_two,NULL,"%s",(tbblue_force_disable_layer_layer_two.v ? "Disabled" : "Enabled "));
			menu_add_item_menu_tabulado(array_menu_tsconf_layer_settings,1,lin);
 			menu_add_item_menu_format(array_menu_tsconf_layer_settings,MENU_OPCION_NORMAL,menu_tbblue_layer_reveal_layer2,NULL,"%s",(tbblue_reveal_layer_layer2.v ? "Reveal" : "Normal"));
			menu_add_item_menu_tabulado(array_menu_tsconf_layer_settings,12,lin);
			lin+=2;

            menu_add_item_menu_format(array_menu_tsconf_layer_settings,MENU_OPCION_NORMAL,menu_tbblue_layer_settings_layer2_priority_bit,NULL,
                "[%c] Allow L2 priority bit",(tbblue_allow_layer2_priority_bit.v ? 'X' : ' '));
            menu_add_item_menu_tabulado(array_menu_tsconf_layer_settings,1,lin);

		}

        else if (MACHINE_HAS_VDP_9918A) {

            //if (!vdp_9918a_si_sms_video_mode4()) {
            if (!MACHINE_IS_SMS) {

                menu_add_item_menu_inicial_format(&array_menu_tsconf_layer_settings,MENU_OPCION_NORMAL,menu_msx_layer_settings_border,NULL,"%s",(vdp_9918a_force_disable_layer_border.v ? "Disabled" : "Enabled "));
                menu_add_item_menu_tabulado(array_menu_tsconf_layer_settings,1,lin);
                lin+=3;

                menu_add_item_menu_format(array_menu_tsconf_layer_settings,MENU_OPCION_NORMAL,menu_msx_layer_settings_ula,NULL,"%s",(vdp_9918a_force_disable_layer_ula.v ? "Disabled" : "Enabled "));
                menu_add_item_menu_tabulado(array_menu_tsconf_layer_settings,1,lin);
                menu_add_item_menu_format(array_menu_tsconf_layer_settings,MENU_OPCION_NORMAL,menu_msx_layer_reveal_ula,NULL,"%s",(vdp_9918a_reveal_layer_ula.v ? "Reveal" : "Normal"));
                menu_add_item_menu_tabulado(array_menu_tsconf_layer_settings,12,lin);
                lin+=3;

                menu_add_item_menu_format(array_menu_tsconf_layer_settings,MENU_OPCION_NORMAL,menu_msx_layer_settings_sprites,NULL,"%s",(vdp_9918a_force_disable_layer_sprites.v ? "Disabled" : "Enabled "));
                menu_add_item_menu_tabulado(array_menu_tsconf_layer_settings,1,lin);
                menu_add_item_menu_format(array_menu_tsconf_layer_settings,MENU_OPCION_NORMAL,menu_msx_layer_reveal_sprites,NULL,"%s",(vdp_9918a_reveal_layer_sprites.v ? "Reveal" : "Normal"));
                menu_add_item_menu_tabulado(array_menu_tsconf_layer_settings,12,lin);
                lin+=3;

            }

            else {
                menu_add_item_menu_inicial_format(&array_menu_tsconf_layer_settings,MENU_OPCION_NORMAL,menu_msx_layer_settings_border,NULL,"%s",(vdp_9918a_force_disable_layer_border.v ? "Disabled" : "Enabled "));
                menu_add_item_menu_tabulado(array_menu_tsconf_layer_settings,1,lin);
                lin+=3;

                menu_add_item_menu_format(array_menu_tsconf_layer_settings,MENU_OPCION_NORMAL,menu_sms_layer_settings_tile_bg,NULL,"%s",(vdp_9918a_force_disable_layer_tile_bg.v ? "Disabled" : "Enabled "));
                menu_add_item_menu_tabulado(array_menu_tsconf_layer_settings,1,lin);
                menu_add_item_menu_format(array_menu_tsconf_layer_settings,MENU_OPCION_NORMAL,menu_sms_layer_reveal_tile_bg,NULL,"%s",(vdp_9918a_reveal_layer_tile_bg.v ? "Reveal" : "Normal"));
                menu_add_item_menu_tabulado(array_menu_tsconf_layer_settings,12,lin);
                lin+=3;



                    menu_add_item_menu_format(array_menu_tsconf_layer_settings,MENU_OPCION_NORMAL,menu_sms_layer_settings_tile_fg,NULL,"%s",(vdp_9918a_force_disable_layer_tile_fg.v ? "Disabled" : "Enabled "));
                    menu_add_item_menu_tabulado(array_menu_tsconf_layer_settings,1,lin);
                    menu_add_item_menu_format(array_menu_tsconf_layer_settings,MENU_OPCION_NORMAL,menu_sms_layer_reveal_tile_fg,NULL,"%s",(vdp_9918a_reveal_layer_tile_fg.v ? "Reveal" : "Normal"));
                    menu_add_item_menu_tabulado(array_menu_tsconf_layer_settings,12,lin);
                    menu_add_item_menu_format(array_menu_tsconf_layer_settings,MENU_OPCION_NORMAL,menu_sms_layer_force_bg_tiles,NULL,"%s",(vdp_9918a_force_bg_tiles.v ? "AlwaysBack": "Normal    " ));
                    menu_add_item_menu_tabulado(array_menu_tsconf_layer_settings,20,lin);
                    lin+=3;


                menu_add_item_menu_format(array_menu_tsconf_layer_settings,MENU_OPCION_NORMAL,menu_msx_layer_settings_sprites,NULL,"%s",(vdp_9918a_force_disable_layer_sprites.v ? "Disabled" : "Enabled "));
                menu_add_item_menu_tabulado(array_menu_tsconf_layer_settings,1,lin);
                menu_add_item_menu_format(array_menu_tsconf_layer_settings,MENU_OPCION_NORMAL,menu_msx_layer_reveal_sprites,NULL,"%s",(vdp_9918a_reveal_layer_sprites.v ? "Reveal" : "Normal"));
                menu_add_item_menu_tabulado(array_menu_tsconf_layer_settings,12,lin);
                lin+=3;


                //vdp_9918a_sms_force_show_column_zero
                menu_add_item_menu_format(array_menu_tsconf_layer_settings,MENU_OPCION_NORMAL,menu_sms_layer_settings_force_col0,NULL,"%s",(vdp_9918a_sms_force_show_column_zero.v ? "Forced" : "Normal"));
                menu_add_item_menu_tabulado(array_menu_tsconf_layer_settings,22,lin);
                lin+=2;

                menu_add_item_menu_format(array_menu_tsconf_layer_settings,MENU_OPCION_NORMAL,menu_sms_layer_settings_lock_scroll_horiz,NULL,"%s",(vdp_9918a_sms_lock_scroll_horizontal.v ? "Locked" : "Normal"));
                menu_add_item_menu_tabulado(array_menu_tsconf_layer_settings,22,lin);
                lin+=2;

                menu_add_item_menu_format(array_menu_tsconf_layer_settings,MENU_OPCION_NORMAL,menu_sms_layer_settings_lock_scroll_vert,NULL,"%s",(vdp_9918a_sms_lock_scroll_vertical.v ? "Locked" : "Normal"));
                menu_add_item_menu_tabulado(array_menu_tsconf_layer_settings,22,lin);
                lin+=2;


            }

        }

        else {
            menu_add_item_menu_inicial_format(&array_menu_tsconf_layer_settings,MENU_OPCION_NORMAL,NULL,NULL,"Machine doesn't have layers");
            menu_add_item_menu_tabulado(array_menu_tsconf_layer_settings,1,lin);
        }


        retorno_menu=menu_dibuja_menu_no_title_lang(&tsconf_layer_settings_opcion_seleccionada,&item_seleccionado,array_menu_tsconf_layer_settings,"TSConf Layers" );


	    if (retorno_menu!=MENU_RETORNO_BACKGROUND) {

                if ((item_seleccionado.tipo_opcion&MENU_OPCION_ESC)==0 && retorno_menu>=0) {
                        //llamamos por valor de funcion
                        if (item_seleccionado.menu_funcion!=NULL) {
                                //printf ("actuamos por funcion\n");
                                item_seleccionado.menu_funcion(item_seleccionado.valor_opcion);


                        }
                }
        }

    } while ( (item_seleccionado.tipo_opcion&MENU_OPCION_ESC)==0 && retorno_menu!=MENU_RETORNO_ESC && !salir_todos_menus && retorno_menu!=MENU_RETORNO_BACKGROUND);


    //En caso de menus tabulados, suele ser necesario esto. Si no, la ventana se quedaria visible
	util_add_window_geometry_compact(ventana);


    if (retorno_menu==MENU_RETORNO_BACKGROUND) {
            zxvision_message_put_window_background();
    }

    else {
        //En caso de menus tabulados, es responsabilidad de este de liberar ventana
        zxvision_destroy_window(ventana);
    }



}










//#define TOTAL_PALETTE_WINDOW_X (menu_origin_x() )
//#define TOTAL_PALETTE_WINDOW_Y 0
#define TOTAL_PALETTE_WINDOW_ANCHO 32
#define TOTAL_PALETTE_WINDOW_ALTO 24
//#define TOTAL_PALETTE_COLORS_PER_WINDOW 16



int menu_display_total_palette_current_palette=0;
int menu_display_total_palette_current_colour=0;

//Si se muestra paleta total o paleta mapeada
int menu_display_total_palette_show_mapped=0;

//Retorna colores totales de una paleta ya sea total o mapeada
int menu_display_total_palette_get_total_colors(void)
{
	int limite;

	if (menu_display_total_palette_show_mapped==0) {
		limite=total_palette_colours_array[menu_display_total_palette_current_palette].total_colores;
	}
	else {
		limite=menu_debug_sprites_total_colors_mapped_palette(menu_display_total_palette_current_palette);
	}

	return limite;
}

zxvision_window *menu_display_total_palette_draw_barras_window;

//Indica si se esta pulsando boton de raton en ventana de Colour Palette
//esto indica posiblemente movimiento o redimensionado de dicha ventana
//en esos casos, mientras se redimensiona, no interesa redibujar todo el contenido
//pues consume mucha cpu y no acaba viendose como se redimensiona
//Esto logicamente no es perfecto pues simplemente pulsando con el raton dentro de la ventana,
//se pensara que se redimensiona y por tanto no dibujara el contenido
int window_colour_palette_left_mouse=0;


int menu_display_total_palette_total_colores_por_ventana=0;

//Muestra lista de colores o barras de colores, para una paleta total, o para la paleta mapeada
int menu_display_total_palette_lista_colores(int linea,int si_barras)
{


    //Redibujar esto solo si no estamos redimensionando o arrastrando ventana
    //esto solo lo hago para evitar sobrecargar la cpu y provocaria que no muestra nada al redimensionar
    if (!window_colour_palette_left_mouse) {

		//printf("Escribir lista colores %d\n",contador_segundo);

		char dumpmemoria[33];

		int linea_color;
		int limite;


		limite=menu_display_total_palette_get_total_colors();

		int current_color;
		int indice_paleta;
		int indice_color_final_rgb;
		int color_final_rgb;

		//int total_colores_mostrar;

		//Borramos lista de colores con espacios por si hay estos de antes
		if (!si_barras) {
			int i;
			for (i=0;i<menu_display_total_palette_total_colores_por_ventana;i++) {
				zxvision_print_string_defaults_fillspc(menu_display_total_palette_draw_barras_window,
					0,3+i,"");
			}
		}


		//total_colores_mostrar=TOTAL_PALETTE_COLORS_PER_WINDOW;

		//Con total visible 24, 16 colores

		//total_colores_mostrar=(menu_display_total_palette_draw_barras_window->visible_height)-8;

		//por si acaso
		//if (total_colores_mostrar<1) total_colores_mostrar=1;

		for (linea_color=0;linea_color<menu_display_total_palette_total_colores_por_ventana &&
				menu_display_total_palette_current_colour+linea_color<limite;
				linea_color++) {

					current_color=menu_display_total_palette_current_colour+linea_color;

					int digitos_hexa;
					int digitos_dec;

					digitos_dec=menu_debug_get_total_digits_dec(limite-1);



					if (menu_display_total_palette_show_mapped==0) {

						indice_paleta=total_palette_colours_array[menu_display_total_palette_current_palette].indice_inicial;
						indice_color_final_rgb=indice_paleta+current_color;
						color_final_rgb=spectrum_colortable_normal[indice_color_final_rgb];



						sprintf (dumpmemoria,"%*d: RGB %06XH",digitos_dec,current_color,color_final_rgb);
					}

					else {
						indice_paleta=menu_debug_sprites_return_index_palette(menu_display_total_palette_current_palette, current_color);
						indice_color_final_rgb=menu_debug_sprites_return_color_palette(menu_display_total_palette_current_palette,current_color);
						color_final_rgb=spectrum_colortable_normal[indice_color_final_rgb];
						digitos_hexa=menu_debug_get_total_digits_hexa((menu_debug_sprites_max_value_mapped_palette(menu_display_total_palette_current_palette))-1);

						int no_mostrar_indice=0;

						//Spectra ni speccy base no usan tabla de paleta
						if (menu_display_total_palette_current_palette==2 || menu_display_total_palette_current_palette==0) no_mostrar_indice=1;

						if (no_mostrar_indice) {
							sprintf (dumpmemoria,"%*d: RGB %06XH",digitos_dec,indice_paleta,color_final_rgb);
						}
						else {
							sprintf (dumpmemoria,"%*d: %0*XH RGB %06XH",digitos_dec,current_color,digitos_hexa,indice_paleta,color_final_rgb);
						}

					}



					int longitud_texto=strlen(dumpmemoria);


					int posicion_barra_color_x=longitud_texto+2;
					int posicion_barra_color_y=3+linea_color;

					//dibujar la barra de color
					if (si_barras) {
                        int ancho_ventana;

                        ancho_ventana=menu_display_total_palette_draw_barras_window->visible_width;

                        //por si acaso
                        if (ancho_ventana<1) ancho_ventana=1;

                        //printf("ancho: %d\n",ancho_ventana);

						menu_dibuja_rectangulo_relleno(menu_display_total_palette_draw_barras_window,
                            posicion_barra_color_x*menu_char_width,posicion_barra_color_y*menu_char_height,
							menu_char_width*(ancho_ventana-longitud_texto-3),menu_char_height,indice_color_final_rgb);
					}

			 		else {
						//menu_escribe_linea_opcion(linea++,-1,1,dumpmemoria);
						zxvision_print_string_defaults_fillspc(menu_display_total_palette_draw_barras_window,1,linea++,dumpmemoria);
					}
		}


    }


	zxvision_draw_window_contents(menu_display_total_palette_draw_barras_window);


	return linea;
}




void menu_display_total_palette_draw_barras(void)
{
    //printf("redibujando\n");
    //if (mouse_is_dragging) printf("arrastrando\n");


    menu_speech_set_tecla_pulsada(); //Si no, envia continuamente todo ese texto a speech


    //Mostrar lista colores
    //si ventana minimizada, no ejecutar todo el codigo de overlay
    if (!menu_display_total_palette_draw_barras_window->is_minimized) {
        //printf("Overlay display palettes0 %d\n",contador_segundo);
        menu_display_total_palette_lista_colores(3,0);
    }

    //Esto tiene que estar despues de escribir la lista de colores, para que se refresque y se vea
    //Si estuviese antes, al mover el cursor hacia abajo dejándolo pulsado, el texto no se vería hasta que no se soltase la tecla

    //si ventana minimizada, no ejecutar todo el codigo de overlay
    if (menu_display_total_palette_draw_barras_window->is_minimized) return;

    //printf("Overlay display palettes %d\n",contador_segundo);

    if (si_complete_video_driver()) {
        //Mostrar colores
        menu_display_total_palette_lista_colores(0,1);
    }
}

void menu_display_total_palette_cursor_arriba(void)
{
	if (menu_display_total_palette_current_colour>0) {
		menu_display_total_palette_current_colour--;
	}
}

void menu_display_total_palette_cursor_abajo(void)
{

	int limite=menu_display_total_palette_get_total_colors();

	if (menu_display_total_palette_current_colour<limite-1) {
		menu_display_total_palette_current_colour++;
	}

}

void menu_display_total_palette_crea_ventana(zxvision_window *ventana,int xventana,int yventana,int ancho_ventana,int alto_ventana,
    int is_minimized,int is_maximized,int ancho_antes_minimize,int alto_antes_minimize)
{
    //zxvision_new_window(ventana,xventana,yventana,ancho_ventana,alto_ventana,ancho_ventana-1,alto_ventana-2,"Colour palettes");

    zxvision_new_window_gn_cim(ventana,xventana,yventana,ancho_ventana,alto_ventana,ancho_ventana-1,alto_ventana-2,"Colour palettes",
        "displaypalettes",is_minimized,is_maximized,ancho_antes_minimize,alto_antes_minimize);

	ventana->can_be_backgrounded=1;
    //Permitir hotkeys desde raton
    ventana->can_mouse_send_hotkeys=1;
	//indicar nombre del grabado de geometria
	//strcpy(ventana->geometry_name,"displaypalettes");
    //restaurar estado minimizado de ventana
    //ventana->is_minimized=is_minimized;

    //decimos que tiene que borrar fondo cada vez al redibujar
    //por tanto es como decirle que no use cache de putchar
    //dado que el fondo de texto es casi todo texto con caracter " " eso borra los pixeles que metemos con overlay del frame anterior
    ventana->must_clear_cache_on_draw=1;
}

zxvision_window zxvision_window_display_palettes;


void menu_display_total_palette(MENU_ITEM_PARAMETERS)
{
	menu_espera_no_tecla();
	menu_reset_counters_tecla_repeticion();

	//zxvision_window ventana;

    zxvision_window *ventana;
    ventana=&zxvision_window_display_palettes;

	//IMPORTANTE! no crear ventana si ya existe. Esto hay que hacerlo en todas las ventanas que permiten background.
	//si no se hiciera, se crearia la misma ventana, y en la lista de ventanas activas , al redibujarse,
	//la primera ventana repetida apuntaria a la segunda, que es el mismo puntero, y redibujaria la misma, y se quedaria en bucle colgado
	//zxvision_delete_window_if_exists(ventana);

    //Crear ventana si no existe
    if (!zxvision_if_window_already_exists(ventana)) {

        int xventana,yventana,ancho_ventana,alto_ventana,is_minimized,is_maximized,ancho_antes_minimize,alto_antes_minimize;

        if (!util_find_window_geometry("displaypalettes",&xventana,&yventana,&ancho_ventana,&alto_ventana,&is_minimized,&is_maximized,&ancho_antes_minimize,&alto_antes_minimize)) {
            //xventana=TOTAL_PALETTE_WINDOW_X;
            //yventana=TOTAL_PALETTE_WINDOW_Y;
            ancho_ventana=TOTAL_PALETTE_WINDOW_ANCHO;
            alto_ventana=TOTAL_PALETTE_WINDOW_ALTO;

            xventana=menu_center_x()-ancho_ventana/2;
            yventana=menu_center_y()-alto_ventana/2;
        }

        //guardar tamanyo inicial para cuando se recrea la ventana indicarlo como tamanyo de antes minimizado
        //int ancho_ventana_inicial=ancho_ventana;
        //int alto_ventana_inicial=alto_ventana;


        menu_display_total_palette_crea_ventana(ventana,xventana,yventana,ancho_ventana,alto_ventana,is_minimized,is_maximized,ancho_antes_minimize,alto_antes_minimize);


        //Para poder controlar redimensionamientos de ventana y recrearla de nuevo
        //No es necesario, pero es mas bonito... asi se recrea la ventana, si era muy pequeña, hacerla mas grande
        //garantiza que se podra leer todo el texto
        //int alto_anterior=alto_ventana;
        //int ancho_anterior=ancho_ventana;
        //Ya NO hace falta esto, pues zxvision ya recrea la ventana al ampliarla

        //int alto_anterior;
        //int ancho_anterior;

        //zxvision_window_save_size(ventana,&ancho_anterior,&alto_anterior);

    }

    //Si ya existe, activar esta ventana
    else {

        zxvision_activate_this_window(ventana);
    }

	zxvision_draw_window(ventana);

	z80_byte tecla;


	int salir=0;


    menu_display_total_palette_draw_barras_window=ventana; //Decimos que el overlay lo hace sobre la ventana que tenemos aqui


    //cambio overlay
    zxvision_set_window_overlay(ventana,menu_display_total_palette_draw_barras);


    //Toda ventana que este listada en zxvision_known_window_names_array debe permitir poder salir desde aqui
    //Se sale despues de haber inicializado overlay y de cualquier otra variable que necesite el overlay
    if (zxvision_currently_restoring_windows_on_start) {
            //printf ("Saliendo de ventana ya que la estamos restaurando en startup\n");
            return;
    }

    do {

        int total_colores_por_ventana=(ventana->visible_height)-8;

        //por si acaso
        if (total_colores_por_ventana<1) total_colores_por_ventana=1;

		//Redibujar esto solo si no estamos redimensionando o arrastrando ventana
		//esto solo lo hago para evitar sobrecargar la cpu y provocaria que no muestra nada al redimensionar
		if (!mouse_left) {
			window_colour_palette_left_mouse=0;

			//printf("Redibujar %d\n",contador_segundo);

			//Forzar a mostrar atajos
			z80_bit antes_menu_writing_inverse_color;
			antes_menu_writing_inverse_color.v=menu_writing_inverse_color.v;
			menu_writing_inverse_color.v=1;

			menu_display_total_palette_total_colores_por_ventana=total_colores_por_ventana;


			menu_speech_reset_tecla_pulsada(); //Que envie a speech

			int linea=0;

			char textoshow[64];

			char nombre_paleta[33];

			if (menu_display_total_palette_show_mapped==0) {
				strcpy(nombre_paleta,total_palette_colours_array[menu_display_total_palette_current_palette].nombre_paleta);
			}
			else {
				menu_debug_sprites_get_palette_name(menu_display_total_palette_current_palette,nombre_paleta);
			}

			sprintf (textoshow,"Palette %d: %s",menu_display_total_palette_current_palette,nombre_paleta);

			zxvision_print_string_defaults_fillspc(ventana,1,linea++,textoshow);

			if (menu_display_total_palette_show_mapped==0) {
				sprintf (textoshow,"%s",total_palette_colours_array[menu_display_total_palette_current_palette].descripcion_paleta);
			}
			else {
				sprintf (textoshow,"Total colours in array: %d",menu_display_total_palette_get_total_colors() );
			}

			zxvision_print_string_defaults_fillspc(ventana,1,linea++,textoshow);


			zxvision_print_string_defaults_fillspc(ventana,1,linea++,"");


			linea +=16;



			zxvision_print_string_defaults_fillspc(ventana,1,linea++,"");

			char buffer_linea[40];

			//Nos situamos justo despues de la lista de colores
			linea=total_colores_por_ventana+3;

			//Y meter una linea en blanco
			zxvision_print_string_defaults(ventana,1,linea++,"");

																// 01234567890123456789012345678901
			sprintf (buffer_linea,"Move: Cursors,Q,A,PgUp,PgDn");


			zxvision_print_string_defaults_fillspc(ventana,1,linea++,buffer_linea);

			sprintf (buffer_linea,"[%c] ~~Mapped palette",(menu_display_total_palette_show_mapped ? 'X' : ' ') );

			zxvision_print_string_defaults_fillspc(ventana,1,linea++,buffer_linea);

			//Restaurar comportamiento atajos
			menu_writing_inverse_color.v=antes_menu_writing_inverse_color.v;

			zxvision_draw_window_contents(ventana);

		}

		else {
			window_colour_palette_left_mouse=1;
		}



		tecla=zxvision_common_getkey_refresh();

        //printf("lee tecla %d\n",contador_segundo);

		int aux_pgdnup;
		int limite;

		switch (tecla) {

			case 11:
				//arriba
				menu_display_total_palette_cursor_arriba();


			break;

			case 10:
				//abajo
				menu_display_total_palette_cursor_abajo();



			break;

			case 24:
				//PgUp
				for (aux_pgdnup=0;aux_pgdnup<total_colores_por_ventana;aux_pgdnup++) {
					menu_display_total_palette_cursor_arriba();
				}

			break;

			case 25:
				//PgDn
				for (aux_pgdnup=0;aux_pgdnup<total_colores_por_ventana;aux_pgdnup++) {
					menu_display_total_palette_cursor_abajo();
				}

			break;

			case 'q':
				if (menu_display_total_palette_current_palette>0) {
					menu_display_total_palette_current_palette--;
					menu_display_total_palette_current_colour=0;
				}


			break;

			case 'a':
				if (menu_display_total_palette_show_mapped==0) {
					limite=TOTAL_PALETAS_COLORES;
				}

				else {
					limite=MENU_TOTAL_MAPPED_PALETTES;
				}

				if (menu_display_total_palette_current_palette<limite-1) {
					menu_display_total_palette_current_palette++;
					menu_display_total_palette_current_colour=0;
				}


			break;

			case 'm':
				menu_display_total_palette_show_mapped ^=1;
				menu_display_total_palette_current_palette=0;
				menu_display_total_palette_current_colour=0;

			break;


			//Salir con ESC
			case 2:
				salir=1;
			break;

			//O tecla background
			case 3:
				salir=1;
			break;
		}

		//Si ha cambiado el tamaño
		//Ya NO hace falta esto, pues zxvision ya recrea la ventana al ampliarla
		/*
		alto_ventana=ventana->visible_height;
		ancho_ventana=ventana->visible_width;
		xventana=ventana->x;
		yventana=ventana->y;
		if (alto_ventana!=alto_anterior || ancho_ventana!=ancho_anterior) {
				//printf ("recrear ventana\n");
				//Recrear ventana
				int is_minimized=ventana->is_minimized;

				//printf("is minimized: %d\n",is_minimized);


				zxvision_destroy_window(ventana);

				alto_anterior=alto_ventana;
				ancho_anterior=ancho_ventana;

				menu_display_total_palette_crea_ventana(ventana,xventana,yventana,ancho_ventana,alto_ventana,is_minimized,ancho_antes_minimize,alto_antes_minimize);


				zxvision_window_save_size(ventana,&ancho_anterior,&alto_anterior);

				zxvision_draw_window(ventana);

				//Indicar tamanyo de antes minimizado, que es el que tenia al inicio
				//dado que se recrea la ventana siempre que cambia tamaño (y si se minimiza tambien),
				//queremos que se indique el tamaño que tenia antes de minimizar por si se deshace el minimizado
				//ventana->height_before_max_min_imize=alto_ventana_inicial;
				//ventana->width_before_max_min_imize=ancho_ventana_inicial;

		}
		*/


	} while (salir==0);

    //Asegurarnos que al salir esto no queda activado, si no, el overlay no dibujaria nada
    window_colour_palette_left_mouse=0;



	util_add_window_geometry_compact(ventana);

	if (tecla==3) {
		zxvision_message_put_window_background();
	}

	else {

		zxvision_destroy_window(ventana);
	}


}

void menu_debug_disassemble_export(int p)
{

	char string_address[10];
	sprintf (string_address,"%XH",p);


    menu_ventana_scanf("Start?",string_address,10);

	menu_z80_moto_int inicio=parse_string_to_number(string_address);

	menu_ventana_scanf("End?",string_address,10);
	menu_z80_moto_int final=parse_string_to_number(string_address);

	if (final<inicio){
		menu_warn_message("End address must be higher or equal than start address");
		return;
	}

	char file_save[PATH_MAX];
	int ret=menu_ask_file_to_save("Destination file","asm",file_save);

	if (!ret) {
		menu_warn_message("Export cancelled");
		return;
	}


	FILE *ptr_asmfile;
    ptr_asmfile=fopen(file_save,"wb");
    if (!ptr_asmfile) {
		debug_printf (VERBOSE_ERR,"Unable to open asm file");
		return;
    }


	char dumpassembler[65];


	int longitud_opcode;

	//ponemos un final de un numero maximo de instrucciones
	//sera igual al tamaño de la zona de memoria
	int limite_instrucciones=menu_debug_memory_zone_size;

	int instrucciones=0;

	for (;inicio<=final && instrucciones<limite_instrucciones;instrucciones++) {

		menu_debug_dissassemble_una_inst_sino_hexa(dumpassembler,inicio,&longitud_opcode,0,0);


		inicio +=longitud_opcode;
		debug_printf (VERBOSE_DEBUG,"Exporting asm: %s",dumpassembler);

		//Agregar salto de linea
		int longitud_linea=strlen(dumpassembler);
		dumpassembler[longitud_linea++]='\n';
		dumpassembler[longitud_linea]=0;
		fwrite(&dumpassembler,1,longitud_linea,ptr_asmfile);
		//zxvision_print_string_defaults_fillspc(&ventana,1,linea,dumpassembler);
	}

	fclose(ptr_asmfile);

	menu_generic_message_splash("Export disassembly","Ok process finished");

}

menu_z80_moto_int menu_debug_disassemble_change_pointer(menu_z80_moto_int p)
{


    char string_address[10];

    sprintf (string_address,"%XH",p);

    menu_ventana_scanf("Address?",string_address,10);

    //Evaluar la dirección como una expresión, así podemos usar registros, sumas, etc
    menu_debug_cpu_calculate_expression(string_address,&p);


    return p;

}

z80_bit menu_debug_disassemble_hexa_view={0};

void menu_debug_disassemble(MENU_ITEM_PARAMETERS)
{

	//printf ("Opening disassemble menu\n");
 	menu_espera_no_tecla();
	menu_reset_counters_tecla_repeticion();

	zxvision_window ventana;

	int ancho_total=32-1;

	if (CPU_IS_MOTOROLA) ancho_total=64-1;

    int ancho_visible=32;
    int alto_visible=20;

    int xventana=menu_center_x()-ancho_visible/2;
    int yventana=menu_center_y()-alto_visible/2;

	zxvision_new_window(&ventana,xventana,yventana,ancho_visible,alto_visible,
							ancho_total,alto_visible-2,"Disassemble");


	//Permitir hotkeys desde raton
	ventana.can_mouse_send_hotkeys=1;

	zxvision_draw_window(&ventana);

    //Inicializar info de tamanyo zona
	menu_debug_set_memory_zone_attr();



	z80_byte tecla;


    menu_z80_moto_int direccion=menu_debug_disassemble_last_ptr;


	do {
		int linea=0;

		int lineas_disass=0;
		const int lineas_total=15;

		char dumpassembler[65];

		int longitud_opcode;
		int longitud_opcode_primera_linea;

		menu_z80_moto_int dir=direccion;

		for (;lineas_disass<lineas_total;lineas_disass++,linea++) {

			//Formato de texto en buffer:
			//0123456789012345678901234567890
			//DDDD AABBCCDD OPCODE-----------
			//DDDD: Direccion
			//AABBCCDD: Volcado hexa

			//Metemos 30 espacios



			//menu_debug_dissassemble_una_instruccion(dumpassembler,dir,&longitud_opcode);
			menu_debug_dissassemble_una_inst_sino_hexa(dumpassembler,dir,&longitud_opcode,menu_debug_disassemble_hexa_view.v,1);


			if (lineas_disass==0) longitud_opcode_primera_linea=longitud_opcode;

			dir +=longitud_opcode;
			zxvision_print_string_defaults_fillspc(&ventana,1,linea,dumpassembler);
		}


	//Forzar a mostrar atajos
	z80_bit antes_menu_writing_inverse_color;
	antes_menu_writing_inverse_color.v=menu_writing_inverse_color.v;
	menu_writing_inverse_color.v=1;



    zxvision_print_string_defaults_fillspc(&ventana,1,linea++,"");

    zxvision_print_string_defaults_fillspc(&ventana,1,linea++,"~~memptr ~~export");
    zxvision_print_string_defaults_fillspc(&ventana,1,linea++,"~~togglehexa");

    zxvision_draw_window_contents(&ventana);


	//Restaurar comportamiento atajos
	menu_writing_inverse_color.v=antes_menu_writing_inverse_color.v;




		tecla=zxvision_common_getkey_refresh();


		int i;

        switch (tecla) {

			case 11:
				//arriba
				direccion=menu_debug_disassemble_subir(direccion);
			break;

			case 10:
				//abajo
				direccion +=longitud_opcode_primera_linea;
			break;

			//No llamamos a zxvision_handle_cursors_pgupdn para solo poder gestionar scroll ventana en horizontal,
			//el resto de teclas (cursores, pgup, dn etc) las gestionamos desde aqui de manera diferente

            //izquierda
            case 8:
				/*
				//Decir que se ha pulsado tecla para que no se relea
				menu_speech_set_tecla_pulsada();*/
				zxvision_handle_cursors_pgupdn(&ventana,tecla);
            break;

            //derecha
            case 9:
				/*
				//Decir que se ha pulsado tecla para que no se relea
				menu_speech_set_tecla_pulsada();*/
				zxvision_handle_cursors_pgupdn(&ventana,tecla);
			break;

			case 24:
				//PgUp
				for (i=0;i<lineas_total;i++) direccion=menu_debug_disassemble_subir(direccion);
			break;

			case 25:
				//PgDn
				direccion=dir;
			break;

			case 'm':
				direccion=menu_debug_disassemble_change_pointer(direccion);
				zxvision_draw_window(&ventana);
			break;

			case 'e':
				menu_debug_disassemble_export(direccion);
				zxvision_draw_window(&ventana);
			break;

			case 't':
				menu_debug_disassemble_hexa_view.v ^=1;
			break;

		}


	} while (tecla!=2);



	zxvision_destroy_window(&ventana);



}


void menu_debug_assemble(MENU_ITEM_PARAMETERS)
{

	//printf ("Opening disassemble menu\n");
 	menu_espera_no_tecla();
	menu_reset_counters_tecla_repeticion();

	zxvision_window ventana;

	int ancho_total=32-1;

	if (CPU_IS_MOTOROLA) ancho_total=64-1;

    int ancho_visible=32;
    int alto_visible=20;

    int xventana=menu_center_x()-ancho_visible/2;
    int yventana=menu_center_y()-alto_visible/2;

	zxvision_new_window(&ventana,xventana,yventana,ancho_visible,alto_visible,
							ancho_total,alto_visible-2,"Assemble");
	zxvision_draw_window(&ventana);

    //Inicializar info de tamanyo zona
	menu_debug_set_memory_zone_attr();



    menu_z80_moto_int direccion=menu_debug_disassemble_last_ptr;

	menu_z80_moto_int direccion_ensamblado=direccion;

	int salir=0;
	int lineas_ensambladas=0;


	do {
		int linea=0;

		int lineas_disass=0;
		const int lineas_total=15;

		char dumpassembler[65];

		int longitud_opcode;
		int longitud_opcode_primera_linea;

		menu_z80_moto_int dir=direccion;

		for (;lineas_disass<lineas_total;lineas_disass++,linea++) {

			//Formato de texto en buffer:
			//0123456789012345678901234567890
			//DDDD AABBCCDD OPCODE-----------
			//DDDD: Direccion
			//AABBCCDD: Volcado hexa

			//Metemos 30 espacios



			//menu_debug_dissassemble_una_instruccion(dumpassembler,dir,&longitud_opcode);
			menu_debug_dissassemble_una_inst_sino_hexa(dumpassembler,dir,&longitud_opcode,menu_debug_disassemble_hexa_view.v,1);


			if (lineas_disass==0) longitud_opcode_primera_linea=longitud_opcode;

			dir +=longitud_opcode;
			zxvision_print_string_defaults_fillspc(&ventana,1,linea,dumpassembler);
		}


		zxvision_draw_window_contents(&ventana);



		char string_opcode[256];
		string_opcode[0]=0;


		char texto_titulo[256];
		sprintf (texto_titulo,"Assemble at %XH",direccion_ensamblado);

    	menu_ventana_scanf(texto_titulo,string_opcode,256);
		zxvision_draw_window(&ventana);

		if (string_opcode[0]==0) salir=1;
		else {


				z80_byte destino_ensamblado[MAX_DESTINO_ENSAMBLADO];


				int longitud_destino=assemble_opcode(direccion_ensamblado,string_opcode,destino_ensamblado);

				if (longitud_destino==0) {
					menu_error_message("Error. Invalid opcode");
					//escribir_socket_format(misocket,"Error. Invalid opcode: %s\n",texto);
					salir=1;
				}

				else {
					menu_debug_set_memory_zone_attr();
					unsigned int direccion_escribir=direccion_ensamblado;
					int i;
					for (i=0;i<longitud_destino;i++) {
						menu_debug_write_mapped_byte(direccion_escribir++,destino_ensamblado[i]);
					}

				}

				direccion_ensamblado+=longitud_destino;

				lineas_ensambladas++;

				//Desensamblar desde la siguiente instruccion si conviene
				if (lineas_ensambladas>5) {
					direccion +=longitud_opcode_primera_linea;
				}

		}

	} while (!salir);





	zxvision_destroy_window(&ventana);



}










void menu_cpu_transaction_log_enable(MENU_ITEM_PARAMETERS)
{
	if (cpu_transaction_log_enabled.v) {
		reset_cpu_core_transaction_log();
	}
	else {

		if (menu_confirm_yesno_texto("May use a lot of disk space","Sure?")==1) {
			set_cpu_core_transaction_log();
        }
	}
}

void menu_cpu_transaction_log_truncate(MENU_ITEM_PARAMETERS)
{
	if (menu_confirm_yesno("Truncate log file")) {
		transaction_log_truncate();
		menu_generic_message("Truncate log file","OK. Log truncated");
	}
}

void menu_cpu_transaction_log_file(MENU_ITEM_PARAMETERS)
{

	if (cpu_transaction_log_enabled.v) reset_cpu_core_transaction_log();

        char *filtros[2];

        filtros[0]="log";
        filtros[1]=0;


        if (menu_filesel("Select Log File",filtros,transaction_log_filename)==1) {
                //Ver si archivo existe y preguntar
		if (si_existe_archivo(transaction_log_filename)) {
                        if (menu_confirm_yesno_texto("File exists","Append?")==0) {
				transaction_log_filename[0]=0;
				return;
			}
                }

        }

	else transaction_log_filename[0]=0;

}


void menu_cpu_transaction_log_enable_address(MENU_ITEM_PARAMETERS)
{
	cpu_transaction_log_store_address.v ^=1;
}

void menu_cpu_transaction_log_enable_datetime(MENU_ITEM_PARAMETERS)
{
        cpu_transaction_log_store_datetime.v ^=1;
}


void menu_cpu_transaction_log_enable_tstates(MENU_ITEM_PARAMETERS)
{
        cpu_transaction_log_store_tstates.v ^=1;
}

void menu_cpu_transaction_log_enable_opcode(MENU_ITEM_PARAMETERS)
{
        cpu_transaction_log_store_opcode.v ^=1;
}

void menu_cpu_transaction_log_enable_registers(MENU_ITEM_PARAMETERS)
{
        cpu_transaction_log_store_registers.v ^=1;
}


void menu_cpu_transaction_log_enable_rotate(MENU_ITEM_PARAMETERS)
{
	cpu_transaction_log_rotate_enabled.v ^=1;
}

void menu_cpu_transaction_log_rotate_number(MENU_ITEM_PARAMETERS)
{

        char string_number[4];

        sprintf (string_number,"%d",cpu_transaction_log_rotated_files);

        menu_ventana_scanf("Number of files",string_number,4);

        int numero=parse_string_to_number(string_number);

		if (transaction_log_set_rotate_number(numero)) {
			debug_printf (VERBOSE_ERR,"Invalid rotation number");
		}


}


void menu_cpu_transaction_log_rotate_size(MENU_ITEM_PARAMETERS)
{


        char string_number[5];

        sprintf (string_number,"%d",cpu_transaction_log_rotate_size);

        menu_ventana_scanf("Size in MB (0=no rot)",string_number,5);

        int numero=parse_string_to_number(string_number);

		if (transaction_log_set_rotate_size(numero)) {
			debug_printf (VERBOSE_ERR,"Invalid rotation size");
		}


}

void menu_cpu_transaction_log_rotate_lines(MENU_ITEM_PARAMETERS)
{


        char string_number[11];

        sprintf (string_number,"%d",cpu_transaction_log_rotate_lines);

        menu_ventana_scanf("Lines (0=no rotate)",string_number,11);

        int numero=parse_string_to_number(string_number);

		if (transaction_log_set_rotate_lines(numero)) {
			debug_printf (VERBOSE_ERR,"Invalid rotation lines");
		}


}

void menu_cpu_transaction_log_ignore_rep_halt(MENU_ITEM_PARAMETERS)
{
	cpu_trans_log_ignore_repeated_halt.v ^=1;
}

void menu_cpu_transaction_log(MENU_ITEM_PARAMETERS)
{
        menu_item *array_menu_cpu_transaction_log;
        menu_item item_seleccionado;
        int retorno_menu;
        do {

                char string_transactionlog_shown[18];
                menu_tape_settings_trunc_name(transaction_log_filename,string_transactionlog_shown,18);

                menu_add_item_menu_inicial_format(&array_menu_cpu_transaction_log,MENU_OPCION_NORMAL,menu_cpu_transaction_log_file,NULL,"Log file [%s]",string_transactionlog_shown );


                if (transaction_log_filename[0]!=0) {
                        menu_add_item_menu_format(array_menu_cpu_transaction_log,MENU_OPCION_NORMAL,menu_cpu_transaction_log_enable,NULL,"[%c] Transaction log enabled",(cpu_transaction_log_enabled.v ? 'X' : ' ' ) );

						menu_add_item_menu_format(array_menu_cpu_transaction_log,MENU_OPCION_NORMAL,menu_cpu_transaction_log_enable_rotate,NULL,"[%c] Autorotate files",(cpu_transaction_log_rotate_enabled.v ? 'X' : ' ' ) );
						menu_add_item_menu_tooltip(array_menu_cpu_transaction_log,"Enable automatic rotation of transaction log files");
						menu_add_item_menu_ayuda(array_menu_cpu_transaction_log,"Enable automatic rotation of transaction log files");
						if (cpu_transaction_log_rotate_enabled.v) {
							menu_add_item_menu_format(array_menu_cpu_transaction_log,MENU_OPCION_NORMAL,menu_cpu_transaction_log_rotate_number,NULL,"[%d] Rotate files",cpu_transaction_log_rotated_files);
							menu_add_item_menu_format(array_menu_cpu_transaction_log,MENU_OPCION_NORMAL,menu_cpu_transaction_log_rotate_size,NULL,"[%d] Rotate size (MB)",cpu_transaction_log_rotate_size);
							menu_add_item_menu_format(array_menu_cpu_transaction_log,MENU_OPCION_NORMAL,menu_cpu_transaction_log_rotate_lines,NULL,"[%d] Rotate lines",cpu_transaction_log_rotate_lines);
						}

						menu_add_item_menu_format(array_menu_cpu_transaction_log,MENU_OPCION_NORMAL,menu_cpu_transaction_log_truncate,NULL,"    Truncate log file");
                }

		menu_add_item_menu_format(array_menu_cpu_transaction_log,MENU_OPCION_NORMAL,menu_cpu_transaction_log_ignore_rep_halt,NULL,"[%c] Ignore repeated halt",(cpu_trans_log_ignore_repeated_halt.v ? 'X' : ' '));


		menu_add_item_menu_format(array_menu_cpu_transaction_log,MENU_OPCION_NORMAL,menu_cpu_transaction_log_enable_datetime,NULL,"[%c] Store Date & Time",(cpu_transaction_log_store_datetime.v ? 'X' : ' '));
		menu_add_item_menu_format(array_menu_cpu_transaction_log,MENU_OPCION_NORMAL,menu_cpu_transaction_log_enable_tstates,NULL,"[%c] Store T-States",(cpu_transaction_log_store_tstates.v ? 'X' : ' '));
		menu_add_item_menu_format(array_menu_cpu_transaction_log,MENU_OPCION_NORMAL,menu_cpu_transaction_log_enable_address,NULL,"[%c] Store Address",(cpu_transaction_log_store_address.v ? 'X' : ' '));
		menu_add_item_menu_format(array_menu_cpu_transaction_log,MENU_OPCION_NORMAL,menu_cpu_transaction_log_enable_opcode,NULL,"[%c] Store Opcode",(cpu_transaction_log_store_opcode.v ? 'X' : ' '));
		menu_add_item_menu_format(array_menu_cpu_transaction_log,MENU_OPCION_NORMAL,menu_cpu_transaction_log_enable_registers,NULL,"[%c] Store Registers",(cpu_transaction_log_store_registers.v ? 'X' : ' '));



               menu_add_item_menu(array_menu_cpu_transaction_log,"",MENU_OPCION_SEPARADOR,NULL,NULL);

                menu_add_ESC_item(array_menu_cpu_transaction_log);

                retorno_menu=menu_dibuja_menu(&cpu_transaction_log_opcion_seleccionada,&item_seleccionado,array_menu_cpu_transaction_log,
                    "CPU Transaction Log Menu","Menú Registro Transacciones CPU","Menú Registre Transaccions CPU" );



                if ((item_seleccionado.tipo_opcion&MENU_OPCION_ESC)==0 && retorno_menu>=0) {
                        //llamamos por valor de funcion
                        if (item_seleccionado.menu_funcion!=NULL) {
                                //printf ("actuamos por funcion\n");
                                item_seleccionado.menu_funcion(item_seleccionado.valor_opcion);

                        }
                }

        } while ( (item_seleccionado.tipo_opcion&MENU_OPCION_ESC)==0 && retorno_menu!=MENU_RETORNO_ESC && !salir_todos_menus);

}



//#define SPRITES_X ( menu_origin_x() )
//#define SPRITES_Y 0
#define SPRITES_ANCHO 32
#define SPRITES_ALTO 14
//#define SPRITES_ALTO_VENTANA (SPRITES_ALTO+10)
#define SPRITES_ALTO_VENTANA 24

menu_z80_moto_int view_sprites_direccion=0;

//ancho en pixeles
z80_int view_sprites_ancho_sprite=8;

//alto en pixeles
z80_int view_sprites_alto_sprite=8*6;


int view_sprites_hardware=0;

z80_bit view_sprites_inverse={0};

//formato pantalla pcw
z80_bit menu_sprites_modo_pcw_pantalla={0};

//Incremento al moverse al siguiente byte
//Normalmente 1 , pero quiza poner a 2 para sprites que se guardan como:
//byte sprite, byte mascara, byte sprite, byte mascara
//Asi podemos saltar el byte de mascara
int view_sprite_incremento=1;

//z80_byte temp_pagina=0xF7;

//pixeles por cada byte. Puede ser 8, 4, 2 o 1
int view_sprites_ppb=8;
//en 8ppb (1 bpp), primer color rotar 7, luego 6, luego 5, .... 0 (8-(pos actual+1)*bpp). mascara 1
//en 4ppb (2 bpp), primer color rotar 6, luego 4, luego 2, luego 0 (8-(pos actual+1)*bpp). mascara 3
//en 2ppb (4 bpp), primer color rotar 4, luego 0  (8-(pos actual+1)*bpp). mascara 15
//en 1ppb (8 bpp), rotar 0 . mascara 255  (8-(pos actual+1)*bpp). mascara 255


//bits per pixel. Puede ser 1, 2, 4, 8
int view_sprites_bpp=1;


//Paletas:
//0: normal spectrum y en adelante
//1: la que esté mapeada en ulaplus
//2: la mapeada en tsconf
int view_sprites_palette=0;


//Sprite esta organizado como memoria de pantalla spectrum
int view_sprites_scr_sprite=0;

int view_sprites_offset_palette=0;

//Si leer color sprites y tiles como formato Master System
//Si vale 1 o 2, ordenan diferente el sprite en pantalla
int view_sprites_sms_tiles=0;


//Retorna total de colores de una paleta mapeada
int menu_debug_sprites_total_colors_mapped_palette(int paleta)
{


	switch (paleta) {

		//Speccy
		case 0:
			return 16;
		break;

		//ULAPLUS
		case 1:
			return 64;
		break;

		//Spectra
		case 2:
			return 64;
		break;

		//CPC
		case 3:
			return 16;
		break;

		//Prism zero
		case 4:
			return 256;
		break;

		//Prism two
		case 5:
			return 256;
		break;

		//Sam
		case 6:
			return 16;
		break;

		//RGB8 Tbblue
		case 7:
		case 8:
		case 9:
		case 10:
		case 11:
		case 12:
		case 13:
		case 14:
			return 256;
		break;

		//TSConf
		case 15:
			return 256;
		break;

		//msx
		case 16:
			return 16;
		break;

		//zxuno prism (paleta de lo que el usuario ha establecido, que no son los colores finales)
		case 17:
			return 16;
		break;

		//zxuno prism (colores finales, a paleta reducida)
		case 18:
			return 16;
		break;

		//SMS
		case 19:
			return VDP_9918A_SMS_MODE4_MAPPED_PALETTE_COLOURS;
		break;

        case 20:
            return PCW_TOTAL_PALETTE_COLOURS;
        break;

	}

	return 16;
}

//Retorna maximo valor para una paleta mapeada, o sea, el total de colores definido en screen.h para esa paleta
int menu_debug_sprites_max_value_mapped_palette(int paleta)
{


	switch (paleta) {

		//Speccy
		case 0:
			return SPECCY_TOTAL_PALETTE_COLOURS;
		break;

		//ULAPLUS
		case 1:
			return ULAPLUS_TOTAL_PALETTE_COLOURS;
		break;

		//Spectra
		case 2:
			return SPECTRA_TOTAL_PALETTE_COLOURS;
		break;

		//CPC
		case 3:
			return CPC_TOTAL_PALETTE_COLOURS;
		break;

		//Prism zero
		case 4:
			return PRISM_TOTAL_PALETTE_COLOURS;
		break;

		//Prism two
		case 5:
			return PRISM_TOTAL_PALETTE_COLOURS;
		break;

		//Sam
		case 6:
			return SAM_TOTAL_PALETTE_COLOURS;
		break;

		//RGB9 Tbblue
		case 7:
		case 8:
		case 9:
		case 10:
		case 11:
		case 12:
		case 13:
		case 14:
			return RGB9_TOTAL_PALETTE_COLOURS;
		break;

		//TSConf
		case 15:
			return TSCONF_TOTAL_PALETTE_COLOURS;
		break;

		//MSX
		case 16:
			return VDP_9918_TOTAL_PALETTE_COLOURS;
		break;


        //zxuno prism (paleta de lo que el usuario ha establecido, que no son los colores finales)
		case 17:
			return 16777216;
		break;

		//ZXuno prism (colores finales, a paleta reducida)
		case 18:
			return TSCONF_TOTAL_PALETTE_COLOURS;
		break;

        case 19:
            return SMS_TOTAL_PALETTE_COLOURS;
        break;

        case 20:
            //PCW
            return TSCONF_TOTAL_PALETTE_COLOURS;
		break;


	}

	return 16;
}


//Retorna indice color asociado a la paleta indicada, que apuntara a la tabla spectrum_colortable_normal
int menu_debug_sprites_return_index_palette(int paleta, z80_byte color)
{

    z80_byte r,g,b;

	switch (paleta) {
		case 0:
			//Speccy
			return color;
		break;

		case 1:
			//ULAPlus
			return ulaplus_palette_table[color%64];

		break;

		case 2:
			//Spectra
			return color%64;
		break;

		case 3:
			//CPC
			return cpc_palette_table[color%16];
		break;

		case 4:
			//Prism zero
			return prism_palette_zero[color%256];
		break;

		case 5:
			//Prism two
			return prism_palette_two[color%256];
		break;

		case 6:
			//Sam
			return (  (sam_palette[color&15]) & 127) ;
		break;

		case 7:
			//TBBlue ula paleta 1
			return tbblue_palette_ula_first[color];
		break;

		case 8:
			//TBBlue ula paleta 2
			return tbblue_palette_ula_second[color];
		break;

		case 9:
			//TBBlue layer2 paleta 1
            // layer 2 palette has extra priority bit in color (must be removed while mixing layers)
			return tbblue_palette_layer2_first[color] & (0xFFFF-TBBLUE_LAYER2_PRIORITY);
		break;

		case 10:
			//TBBlue layer2 paleta 2
            // layer 2 palette has extra priority bit in color (must be removed while mixing layers)
			return tbblue_palette_layer2_second[color] & (0xFFFF-TBBLUE_LAYER2_PRIORITY);
		break;

		case 11:
			//TBBlue sprites paleta 1
			return tbblue_palette_sprite_first[color];
		break;

		case 12:
			//TBBlue sprites paleta 2
			return tbblue_palette_sprite_second[color];
		break;

		case 13:
			//TBBlue tilemap paleta 1
			return tbblue_palette_tilemap_first[color];
		break;

		case 14:
			//TBBlue tilemap paleta 2
			return tbblue_palette_tilemap_second[color];
		break;

		case 15:
			//TSConf
			return tsconf_return_cram_color(color);
		break;

		case 16:
			//MSX
			return color; //Dado que realmente no hay mapeo
		break;

        //zxuno prism (paleta de lo que el usuario ha establecido, que no son los colores finales)
        case 17:
            r=zxuno_prism_current_palette[color].rgb[0];
            g=zxuno_prism_current_palette[color].rgb[1];
            b=zxuno_prism_current_palette[color].rgb[2];
			return (r<<16)|(g<<8)|b;
		break;

		case 18:
			//zxuno prism (colores finales, a paleta reducida)
			return zxuno_prism_current_palette[color].index_palette_15bit;
		break;


        case 19:
            //SMS Mode 4
            return vdp_9918a_sms_cram[color];
        break;

        case 20:
            //PCW
            return pcw_get_color_palette(color);
        break;


	}

	return color;
}

//Retorna valor de color asociado a la paleta actual mapeada
int menu_debug_sprites_return_color_palette(int paleta, z80_byte color)
{

	int index;

	index=menu_debug_sprites_return_index_palette(paleta, color);

	switch (paleta) {
		case 0:
			return index;
		break;

		case 1:
			return ULAPLUS_INDEX_FIRST_COLOR+index;
		break;

		case 2:
			return SPECTRA_INDEX_FIRST_COLOR+index;
		break;

		case 3:
			return CPC_INDEX_FIRST_COLOR+index;
		break;

		case 4:
			return PRISM_INDEX_FIRST_COLOR+index;
		break;

		case 5:
			return PRISM_INDEX_FIRST_COLOR+index;
		break;

		case 6:
			return SAM_INDEX_FIRST_COLOR+index;
		break;

		case 7:
		case 8:
		case 9:
		case 10:
		case 11:
		case 12:
		case 13:
		case 14:
			return RGB9_INDEX_FIRST_COLOR+index;
		break;

		case 15:
			return TSCONF_INDEX_FIRST_COLOR+index;
		break;

		case 16:
			return VDP_9918_INDEX_FIRST_COLOR+index;
		break;


        //zxuno prism (paleta de lo que el usuario ha establecido, que no son los colores finales)
        case 17:
            return TSCONF_INDEX_FIRST_COLOR+zxuno_prism_current_palette[color].index_palette_15bit;
        break;

		case 18:
			//zxuno prism (colores finales, a paleta reducida)
			return TSCONF_INDEX_FIRST_COLOR+index;
		break;

		case 19:
			return SMS_INDEX_FIRST_COLOR+index;
		break;

		case 20:
			//PCW
			return TSCONF_INDEX_FIRST_COLOR+index;
		break;

	}

	return color;
}



void menu_debug_sprites_change_palette(void)
{
	view_sprites_palette++;
	if (view_sprites_palette==MENU_TOTAL_MAPPED_PALETTES) view_sprites_palette=0;
}

//Obtener nombre de una paleta mapeada
void menu_debug_sprites_get_palette_name(int paleta, char *s)
{
	switch (paleta) {
		case 0:
			strcpy(s,"Speccy");
		break;

		case 1:
			strcpy(s,"ULAPlus");
		break;

		case 2:
			strcpy(s,"Spectra");
		break;

		case 3:
			strcpy(s,"CPC");
		break;

		case 4:
			strcpy(s,"Prism Zero");
		break;

		case 5:
			strcpy(s,"Prism Two");
		break;

		case 6:
			strcpy(s,"Sam Coupe");
		break;

		case 7:
			strcpy(s,"TBBlue ULA 1");
		break;

		case 8:
			strcpy(s,"TBBlue ULA 2");
		break;

		case 9:
			strcpy(s,"TBBlue Layer2 1");
		break;

		case 10:
			strcpy(s,"TBBlue Layer2 2");
		break;

		case 11:
			strcpy(s,"TBBlue Sprites 1");
		break;

		case 12:
			strcpy(s,"TBBlue Sprites 2");
		break;

		case 13:
			strcpy(s,"TBBlue Tilemap 1");
		break;

		case 14:
			strcpy(s,"TBBlue Tilemap 2");
		break;

		case 15:
			strcpy(s,"TSConf");
		break;

		case 16:
			strcpy(s,"VDP9918A");
		break;


        //(paleta de lo que el usuario ha establecido, que no son los colores finales)
		case 17:
			strcpy(s,"ZX-Uno Prism");
		break;

        //colores finales, a paleta reducida
		case 18:
			strcpy(s,"ZX-Uno Prism (final)");
		break;

		case 19:
			strcpy(s,"SMS Mode 4");
		break;

		case 20:
			strcpy(s,"PCW");
		break;

		default:
			strcpy(s,"UNKNOWN");
		break;



	}
}


void menu_debug_sprites_change_bpp(void)
{
//pixeles por cada byte. Puede ser 8, 4, 2 o 1
//int view_sprites_ppb=8;
//en 8ppb (1 bpp), primer color rotar 7, luego 6, luego 5, .... 0 (8-(pos actual+1)*bpp). mascara 1
//en 4ppb (2 bpp), primer color rotar 6, luego 4, luego 2, luego 0 (8-(pos actual+1)*bpp). mascara 3
//en 2ppb (4 bpp), primer color rotar 4, luego 0  (8-(pos actual+1)*bpp). mascara 15
//en 1ppb (8 bpp), rotar 0 . mascara 255  (8-(pos actual+1)*bpp). mascara 255


//bits per pixel. Puede ser 1, 2, 4, 8
//int view_sprites_bpp=1;
	view_sprites_bpp=view_sprites_bpp <<1;
	view_sprites_ppb=view_sprites_ppb >>1;

	if (view_sprites_bpp>8) {
		view_sprites_bpp=1;
		view_sprites_ppb=8;
	}

	//En algun caso, cambiando de maquina SG1000 a MSX, parece que este view_sprites_ppb llega a ser 0
	//Dado que es un parametro que se utiliza para dividir, hacer que nunca pueda ser cero
	if (view_sprites_ppb==0) view_sprites_ppb=1;

	//printf ("bpp: %d ppb: %d\n",view_sprites_bpp,view_sprites_ppb);
}


menu_z80_moto_int menu_debug_draw_sprites_get_pointer_offset(int direccion)
{

	menu_z80_moto_int puntero;

	puntero=direccion; //por defecto

	if (view_sprites_hardware) {

		if (MACHINE_IS_TSCONF) {
			//view_sprites_direccion-> numero sprite
			struct s_tsconf_debug_sprite spriteinfo;

			tsconf_get_debug_sprite(view_sprites_direccion,&spriteinfo);

		        int ancho_linea=256; //512 pixeles a 4bpp
			int tnum_x=spriteinfo.tnum_x;
			int tnum_y=spriteinfo.tnum_y;

	                tnum_x *=8;
        	        tnum_y *=8;

                	//a 4bpp
	                tnum_x /=2;



        	        puntero=(tnum_y*ancho_linea)+tnum_x;

		}

		if (MACHINE_IS_TBBLUE) {
			if (view_sprites_bpp==4) puntero=view_sprites_direccion*TBBLUE_SPRITE_4BPP_SIZE;
			else puntero=view_sprites_direccion*TBBLUE_SPRITE_8BPP_SIZE;
		}


		//if (MACHINE_HAS_VDP_9918A) {
		//	puntero=view_sprites_direccion+vdp_9918a_get_pattern_base_address();
		//}

	}


	return puntero;

}

zxvision_window *menu_debug_draw_sprites_window;

int view_sprites_bytes_por_linea=1;
int view_sprites_bytes_por_ventana=1;
int view_sprites_increment_cursor_vertical=1;

z80_bit view_sprites_zx81_pseudohires={0}; //Si utiliza puntero a tabla de la rom, como los usados en juegos hires de zx81 (ejemplo rocketman)

z80_byte menu_debug_draw_sprites_get_byte(menu_z80_moto_int puntero)
{

	//printf ("menu_debug_draw_sprites_get_byte 1\n");
	z80_byte byte_leido;

					puntero=adjust_address_memory_size(puntero);

					//printf ("menu_debug_draw_sprites_get_byte 2\n");
				byte_leido=menu_debug_get_mapped_byte(puntero);
					//printf ("menu_debug_draw_sprites_get_byte 3\n");


				//Si hay puntero a valores en rom como algunos juegos pseudo hires de zx81
				if (view_sprites_zx81_pseudohires.v) {
					int temp_inverse=0; //si se hace inverse derivado de juegos pseudo hires de zx81
					if (byte_leido&128) temp_inverse=1;

					z80_int temp_dir=reg_i*256+(8*(byte_leido&63));
					byte_leido=peek_byte_no_time(temp_dir);

					if (temp_inverse) byte_leido ^=255;
				}


				if (view_sprites_inverse.v) {
					byte_leido ^=255;
				}

	return byte_leido;
}

int menu_draw_sprites_get_origin_x(void)
{
	int sx=1;
	//int sx=SPRITES_X+1;
	//int sx=1;
	//int sy=SPRITES_Y+3;

	//Si es mas ancho, que ventana visible, mover coordenada x 1 posicion atrás
	//if (view_sprites_ancho_sprite/menu_char_width>=SPRITES_ANCHO-2) sx--;

	//Si es mas alto, mover coordenada sprite a 1, asi podemos mostrar sprites de hasta 192 de alto
	//if (view_sprites_alto_sprite>168) sy=1;

	//Si se pasa aun mas
	//if (view_sprites_alto_sprite>184) sy=0;

        int xorigen=sx*menu_char_width;

    return xorigen;
}

int menu_draw_sprites_get_origin_y(void)
{
	//int sx=SPRITES_X+1;
	//int sx=1;
	//int sy=SPRITES_Y+3;

	//Si es mas ancho, que ventana visible, mover coordenada x 1 posicion atrás
	//if (view_sprites_ancho_sprite/menu_char_width>=SPRITES_ANCHO-2) sx--;

	//Si es mas alto, mover coordenada sprite a 1, asi podemos mostrar sprites de hasta 192 de alto
	//if (view_sprites_alto_sprite>168) sy=1;

	//Si se pasa aun mas
	//if (view_sprites_alto_sprite>184) sy=0;
    int yorigen=menu_char_height*2;

    return yorigen;
}

//Borrar zona ocupada por los sprites
/*void menu_draw_sprites_clear(zxvision_window *w)
{
    zxvision_draw_filled_rectangle(w,menu_draw_sprites_get_origin_x(),menu_draw_sprites_get_origin_y(),
        view_sprites_ancho_sprite,view_sprites_alto_sprite,ESTILO_GUI_PAPEL_NORMAL);
}*/

int menu_debug_draw_sprites_zoom_sprites=1;
int menu_debug_draw_sprites_grid=0;

void menu_debug_draw_sprites(void)
{



	menu_speech_set_tecla_pulsada(); //Si no, envia continuamente todo ese texto a speech


    //si ventana minimizada, no ejecutar todo el codigo de overlay
    if (menu_debug_draw_sprites_window->is_minimized) return;

    //Visor de sprites hardware no soporta zoom de momento (habria que repensar los bucles "for" para que esto funcionase)
    if (view_sprites_hardware) menu_debug_draw_sprites_zoom_sprites=1;

	//int sx=SPRITES_X+1;
	//int sx=1;
	//int sy=SPRITES_Y+3;

	//Si es mas ancho, que ventana visible, mover coordenada x 1 posicion atrás
	//if (view_sprites_ancho_sprite/menu_char_width>=SPRITES_ANCHO-2) sx--;

	//Si es mas alto, mover coordenada sprite a 1, asi podemos mostrar sprites de hasta 192 de alto
	//if (view_sprites_alto_sprite>168) sy=1;

	//Si se pasa aun mas
	//if (view_sprites_alto_sprite>184) sy=0;

    //int xorigen=sx*menu_char_width;

    int xorigen=menu_draw_sprites_get_origin_x();

    int yorigen=menu_draw_sprites_get_origin_y();


    int x,y,bit;
	z80_byte byte_leido;

	menu_z80_moto_int puntero;

	//puntero=view_sprites_direccion;

	//Ajustar valor puntero en caso de sprites tsconf por ejemplo
	puntero=menu_debug_draw_sprites_get_pointer_offset(view_sprites_direccion);


	int color;

	int finalx;

	menu_z80_moto_int puntero_inicio_linea;

	//int maximo_visible_x=32*menu_char_width;



		int tamanyo_linea=view_sprites_bytes_por_linea;



		for (y=0;y<view_sprites_alto_sprite*menu_debug_draw_sprites_zoom_sprites;y+=menu_debug_draw_sprites_zoom_sprites) {
			if (view_sprites_scr_sprite && y<192) {
				 puntero=view_sprites_direccion+screen_addr_table[(y<<5)];
			}

			puntero_inicio_linea=puntero;
			finalx=xorigen;

			menu_z80_moto_int puntero_final;

            //Para sprites sms modo 4
            z80_byte byte_leido_sms_1,byte_leido_sms_2,byte_leido_sms_3,byte_leido_sms_4;



			for (x=0;x<view_sprites_ancho_sprite;) {
				//printf ("puntero: %d\n",puntero);
				puntero=adjust_address_memory_size(puntero);




				puntero_final=puntero;

                //Hacer esto a cada salto de x 0,8, etc
                if (view_sprites_sms_tiles && (x%8)==0) {
                    //Caso de sprites Master System modo 4

                    if (view_sprites_hardware) {
                        //Activado setting de hardware.

                        //Accedemos a la tabla de 64 sprites

                        //menu_z80_moto_int puntero_orig=menu_debug_draw_sprites_get_pointer_offset(view_sprites_direccion);

                        z80_int attribute_table=vdp_9918a_get_sprite_attribute_table();

                        int numero_sprite=menu_debug_draw_sprites_get_pointer_offset(view_sprites_direccion);

                        numero_sprite %=VDP_9918A_SMS_MODE4_MAX_SPRITES;



                        attribute_table +=0x80+numero_sprite*2;

                        //printf ("tabla atributo sprite: %04XH\n",attribute_table);

                        //printf ("antes\n");
                        //Obtener byte 2, sprite name
                        z80_byte sprite_name=menu_debug_draw_sprites_get_byte(attribute_table+1);

                        //printf ("numero sprite: %d sprite name: %d\n",numero_sprite,sprite_name);

                        //Si ancho > 8, a la derecha mostramos el siguiente sprite

                        int offset_sprite=sprite_name;

                        int offset_pattern_table=offset_sprite*32+vdp_9918a_get_sprite_pattern_table_sms_mode4();
                        puntero_final=offset_pattern_table+y*4;
                    }

                    else {
                        //Ejemplo para sprite de ancho 32 y alto 16,
                        //Mostrar el sprite ordenado asi: (Modo "A"). En pantalla lo indica con ">" de flecha derecha
                        //Asi se ve bien en Streets of Rage
                        // 0 1 2 3
                        // 4 5 6 7
                        //donde 0 es el primer sprite, 1 el segundo, etc

                        //a cada "salto" de columna salta 32 bytes (1 sprite)
                        //a cada "salto" de fila salta (en este ejemplo) 32*4

                        //Modo "B": En pantalla lo indica con "v" de flecha abajo
                        //Asi se ve bien en Sonic
                        // 0 2 4 6
                        // 1 3 5 7

                        //Nota: en caso de sprites de 8x8 sera:
                        // 0

                        //Y en caso de 8x16 sera:
                        // 0
                        // 1

                        //Por tanto en esos casos no le afecta el modo A o B. En sprites por hardware es 8x8 o 8x16 por tanto da igual el modo A o B
                        //Lo que haremos en ese modo hardware es no mostrar > o v, simplemente activo (X)


                        int fila=y/8;
                        int columna=x/8;

                        int tamanyo_sprite=32;


                        //Usado en modo "A"
                        int sprites_en_fila=view_sprites_ancho_sprite/8;

                        //Usado en modo "B"
                        int sprites_en_columna=view_sprites_alto_sprite/8;

                        int offset_sprite;

                        if (view_sprites_sms_tiles==1) {
                            //Donde apunta el principio del sprite. modo A
                            offset_sprite=(fila*sprites_en_fila)+columna;
                        }

                        else {
                            //view_sprites_sms_tiles sera 2
                            //Donde apunta el principio del sprite. modo B
                            offset_sprite=(columna*sprites_en_columna)+fila;
                        }



                        offset_sprite *=tamanyo_sprite;

                        //Aqui estaremos siempre a principio de columna (x divisible entre 8)
                        //sumamos y
                        offset_sprite +=(y & 7)*4;


                        //int incremento_linea=(y/8)+
                        //int offset_linea=(view_sprites_ancho_sprite/8)*32;
                        puntero_final=view_sprites_direccion+offset_sprite;



                    }

                    byte_leido_sms_1=menu_debug_draw_sprites_get_byte(puntero_final++);
                    byte_leido_sms_2=menu_debug_draw_sprites_get_byte(puntero_final++);
                    byte_leido_sms_3=menu_debug_draw_sprites_get_byte(puntero_final++);
                    byte_leido_sms_4=menu_debug_draw_sprites_get_byte(puntero_final++);

                }

				//Alterar en el caso de VDP9918A, que es un tanto particular (sobretodo 16x16)
				if (view_sprites_hardware && MACHINE_HAS_VDP_9918A) {

                    if (!view_sprites_sms_tiles) {

                        //Accedemos a la tabla de 32 sprites

                        //menu_z80_moto_int puntero_orig=menu_debug_draw_sprites_get_pointer_offset(view_sprites_direccion);

                        z80_int attribute_table=vdp_9918a_get_sprite_attribute_table();

                        int numero_sprite=menu_debug_draw_sprites_get_pointer_offset(view_sprites_direccion);

                        numero_sprite %=32;

                        //printf ("numero sprite: %d\n",numero_sprite);

                        attribute_table +=numero_sprite*4;

                        //printf ("tabla atributo sprite: %04XH\n",attribute_table);

                        //printf ("antes\n");
                        //Obtener byte 2, sprite name
                        z80_byte sprite_name=menu_debug_draw_sprites_get_byte(attribute_table+2);


                        //printf ("despues\n");

                        //TODO: asumimos sprites 16x16
                        //TODO: colores del sprite

                        menu_z80_moto_int puntero_orig=sprite_name *8;
                        /*
                        QUADRANT   AC
                                BD

                        Orden en memoria:
                        A   0
                        B   8
                        C   16
                        D   24
                        */
                        //y<8

                        //Quad A
                        if (y<=7 && x<=7) {
                            puntero_final=puntero_orig+y;
                        }

                        //Quad B
                        else if (y>=8 && y<=15 && x<=7) {
                            puntero_final=puntero_orig+y;
                        }

                        //Quad C
                        else if (y<=7 && x>=8 && x<=15) {
                            puntero_final=puntero_orig+16+y;
                        }

                        //Quad D
                        else  {
                            puntero_final=puntero_orig+16+y;
                        }


                        puntero_final +=vdp_9918a_get_sprite_pattern_table();

                        //printf ("puntero final: %04XH\n",puntero_final);


                    }


				}

				byte_leido=menu_debug_draw_sprites_get_byte(puntero_final);

				/*byte_leido=menu_debug_get_mapped_byte(puntero);



				//Si hay puntero a valores en rom como algunos juegos pseudo hires de zx81
				if (view_sprites_zx81_pseudohires.v) {
					int temp_inverse=0; //si se hace inverse derivado de juegos pseudo hires de zx81
					if (byte_leido&128) temp_inverse=1;

					z80_int temp_dir=reg_i*256+(8*(byte_leido&63));
					byte_leido=peek_byte_no_time(temp_dir);

					if (temp_inverse) byte_leido ^=255;
				}


				if (view_sprites_inverse.v) {
					byte_leido ^=255;
				}
				*/

                if (menu_sprites_modo_pcw_pantalla.v && MACHINE_IS_PCW) puntero +=8;

				else puntero +=view_sprite_incremento;


				int incx=0;

                int total_bpp=view_sprites_bpp;

                //para sms, solo hacer este bucle 1 vez
                if (view_sprites_sms_tiles) total_bpp=8;

				for (bit=0;bit<8;bit+=total_bpp,incx++,finalx+=menu_debug_draw_sprites_zoom_sprites,x++) {




					int dis=(8-(incx+1)*view_sprites_bpp);

					//printf ("incx: %d dis: %d\n",incx,dis);

					color=byte_leido >> dis;
					z80_byte mascara;
					switch (view_sprites_bpp) {
						case 1:
							mascara=1;
						break;

						case 2:
							mascara=3;
						break;

						case 4:
							mascara=15;
						break;

						case 8:
							mascara=255;
						break;
					}

					color=color & mascara;

				//en 8ppb (1 bpp), primer color rotar 7, luego 6, luego 5, .... 0 (8-(pos actual+1)*bpp). mascara 1
				//en 4ppb (2 bpp), primer color rotar 6, luego 4, luego 2, luego 0 (8-(pos actual+1)*bpp). mascara 3
				//en 2ppb (4 bpp), primer color rotar 4, luego 0  (8-(pos actual+1)*bpp). mascara 15
				//en 1ppb (8 bpp), rotar 0 . mascara 255  (8-(pos actual+1)*bpp). mascara 255

				//Caso 1 bpp
				if (view_sprites_bpp==1) {
                                        if ( color ==0 ) color=ESTILO_GUI_PAPEL_NORMAL;
                                        else color=ESTILO_GUI_TINTA_NORMAL;
				}
				else {
					color=menu_debug_sprites_return_color_palette(view_sprites_palette,color+view_sprites_offset_palette);

				}

                //En el caso de modo sms sprites modo 4, lo anterior realizado no sirve de mucho, pues vamos a sacar el color del pixel de otra manera
                //TODO: hacer que el código anterior no se ejecute si estamos en dicho modo, realmente no afecta pero es absurdo ejecutar eso
                //si el color lo recalculamos de otra manera
                if (view_sprites_sms_tiles) {
                    z80_byte byte_color=((byte_leido_sms_1>>7)&1) | ((byte_leido_sms_2>>6)&2) | ((byte_leido_sms_3>>5)&4) | ((byte_leido_sms_4>>4)&8);


                    z80_byte color_sprite=vdp_9918a_sms_cram[view_sprites_offset_palette + (byte_color & 15)] & 63;

                    color=SMS_INDEX_FIRST_COLOR+color_sprite;

                }


                //Contemplar zoom de sprites
                int zx,zy;
                for (zx=0;zx<menu_debug_draw_sprites_zoom_sprites;zx++) {
                    for (zy=0;zy<menu_debug_draw_sprites_zoom_sprites;zy++) {
                        //Contemplar rejilla a partir de zoom 4
                        int color_pixel=color;
                        if (menu_debug_draw_sprites_zoom_sprites>=4 && menu_debug_draw_sprites_grid) {
                            //cuadricula
                            if (zx==0 || zy==0) {
                                /*
                                if (view_sprites_bpp==1) {
                                    if (color_pixel==ESTILO_GUI_PAPEL_NORMAL) color_pixel=ESTILO_GUI_TINTA_NORMAL;
                                    else color_pixel=ESTILO_GUI_PAPEL_NORMAL;
                                }

                                else color_pixel=ESTILO_GUI_TINTA_NORMAL;
                                */

                                //La cuadricula con el color de waveform siempre se vera bien encima de un fondo de papel

                                color_pixel=ESTILO_GUI_COLOR_WAVEFORM;
                            }
                        }
				        zxvision_putpixel(menu_debug_draw_sprites_window,finalx+zx,yorigen+y+zy,color_pixel);
                    }
                }

			   }

                byte_leido_sms_1=byte_leido_sms_1<<1;
                byte_leido_sms_2=byte_leido_sms_2<<1;
                byte_leido_sms_3=byte_leido_sms_3<<1;
                byte_leido_sms_4=byte_leido_sms_4<<1;

			}

			//puntero=puntero_inicio_linea;


            if (menu_sprites_modo_pcw_pantalla.v && MACHINE_IS_PCW) {
                //Cada 8 lineas, se incrementa diferente
                if ((y%8)==7) {
                    //puntero+=view_sprites_ancho_sprite;
                    //puntero+=(720/8);
                    //Ya viene incrementado +8. Entonces se deberia incrementar en +1
                    puntero-=7;
                }
                else {
                    puntero=puntero_inicio_linea;
                    puntero++;
                }
            }
			else {
                puntero=puntero_inicio_linea;
                puntero +=tamanyo_linea;
            }


		}


	zxvision_draw_window_contents(menu_debug_draw_sprites_window);

}

char *menu_debug_sprites_change_ptr_historial[UTIL_SCANF_HISTORY_MAX_LINES]={
    NULL
};

menu_z80_moto_int menu_debug_view_sprites_change_pointer(menu_z80_moto_int p)
{




        char string_address[10];


        int tecla;

				if (view_sprites_hardware) {
					sprintf(string_address,"%d",p&63);
					//menu_ventana_scanf("Sprite?",string_address,3);
                    tecla=zxvision_scanf_history("Sprite?",string_address,3,menu_debug_sprites_change_ptr_historial);
				}
				else {
					util_sprintf_address_hex(p,string_address);
        	        //menu_ventana_scanf("Address?",string_address,10);
                    tecla=zxvision_scanf_history("Address?",string_address,10,menu_debug_sprites_change_ptr_historial);
				}



        //p=parse_string_to_number(string_address);
        //Evaluar la dirección como una expresión, así podemos usar registros, sumas, etc

        if (tecla!=2) {
            menu_debug_cpu_calculate_expression(string_address,&p);
        }




        return p;

}





//Retorna 0 si ok
int menu_debug_view_sprites_save(menu_z80_moto_int direccion,int ancho, int alto, int ppb, int incremento)
{

	char file_save[PATH_MAX];

	char *filtros[3];

						 filtros[0]="pbm";
						 filtros[1]="c";
						 filtros[2]=0;

		int ret;

		 ret=menu_filesel_save("Select PBM/C File",filtros,file_save);

		 if (ret==1) {

 		 		//Ver si archivo existe y preguntar
			 if (si_existe_archivo(file_save)) {

	 				if (menu_confirm_yesno_texto("File exists","Overwrite?")==0) return 0;

 				}


				char string_height[4];
				sprintf(string_height,"%d",alto);
				menu_ventana_scanf("Height?",string_height,4);
				alto=parse_string_to_number(string_height);


				//Asignar buffer temporal
				int longitud=ancho*alto;
				z80_byte *buf_temp=malloc(longitud);
				if (buf_temp==NULL) {
					debug_printf(VERBOSE_ERR,"Error allocating temporary buffer");
				}

				//Copiar de memoria emulador ahi
				int i;
				for (i=0;i<longitud;i++) {
					//buf_temp[i]=peek_byte_z80_moto(direccion);
					buf_temp[i]=menu_debug_draw_sprites_get_byte(direccion);
					direccion +=incremento;
				}


				if (!util_compare_file_extension(file_save,"pbm")) {
					util_write_pbm_file(file_save,ancho,alto,ppb,buf_temp);
				}

				else if (!util_compare_file_extension(file_save,"c")) {
					util_write_sprite_c_file(file_save,ancho,alto,ppb,buf_temp);
				}

				else {
					//debug_printf (VERBOSE_ERR,"Error. Unkown sprite file format");
					return 1;
				}

				free(buf_temp);
		}

	return 0;

}


void menu_debug_sprites_get_parameters_hardware(void)
{
	if (view_sprites_hardware) {
		if (MACHINE_IS_TBBLUE) {
			//Parametros que alteramos segun sprite activo


                	view_sprites_ancho_sprite=16;

	                view_sprites_alto_sprite=16;



			//offset paleta
			view_sprites_offset_palette=0;

			view_sprites_increment_cursor_vertical=1; //saltar de 1 en 1






			//Permitir 4 u 8 bpp
			if (view_sprites_bpp!=8 && view_sprites_bpp!=4) {
				view_sprites_bpp=4;
				view_sprites_ppb=2;
			}


            view_sprites_bytes_por_linea=16/view_sprites_ppb;


			view_sprites_bytes_por_ventana=8;


			//Cambiar a zona memoria 14. TBBlue sprites
			//while (menu_debug_memory_zone!=14) menu_debug_change_memory_zone_non_interactive();

			menu_debug_set_memory_zone(14);

			//paleta 11 tbblue
			//view_sprites_palette=11;


		}

		if (MACHINE_IS_TSCONF) {

			//Parametros que alteramos segun sprite activo
	                struct s_tsconf_debug_sprite spriteinfo;

        	        tsconf_get_debug_sprite(view_sprites_direccion,&spriteinfo);

                	view_sprites_ancho_sprite=spriteinfo.xs;

	                view_sprites_alto_sprite=spriteinfo.ys;

			//offset paleta
			view_sprites_offset_palette=spriteinfo.spal*16;


			view_sprites_increment_cursor_vertical=1; //saltar de 1 en 1

                        view_sprites_bytes_por_linea=256;

			view_sprites_bytes_por_ventana=8; //saltar de 8 en 8 con pgdn/up


			view_sprites_bpp=4;
			view_sprites_ppb=2;




			//Cambiar a zona memoria 15. TSConf sprites
			//while (menu_debug_memory_zone!=15) menu_debug_change_memory_zone_non_interactive();

			menu_debug_set_memory_zone(15);


			//paleta 13 tsconf
			//view_sprites_palette=13;




		}

		if (MACHINE_HAS_VDP_9918A) {
//Parametros que alteramos segun sprite activo


                	view_sprites_ancho_sprite=16;

	                view_sprites_alto_sprite=16;



			//offset paleta
			view_sprites_offset_palette=0;

			view_sprites_increment_cursor_vertical=1; //saltar de 1 en 1


            if (view_sprites_sms_tiles) {
                //4 bpp en caso de modo 4 sms
			    view_sprites_bpp=4;
			    view_sprites_ppb=2;

                	view_sprites_ancho_sprite=8;

	                view_sprites_alto_sprite=vdp_9918a_sms_get_sprite_height();

                //Indicamos el offset de la paleta a 16, el de sprites
                view_sprites_offset_palette=16;
            }

            else {
			    //Permitir solo 1bpp

			    view_sprites_bpp=1;
			    view_sprites_ppb=8;
            }



            view_sprites_bytes_por_linea=16/view_sprites_ppb;


			view_sprites_bytes_por_ventana=8;


			if (MACHINE_IS_MSX) menu_debug_set_memory_zone(MEMORY_ZONE_MSX_VRAM);
			if (MACHINE_IS_COLECO) menu_debug_set_memory_zone(MEMORY_ZONE_COLECO_VRAM);
			if (MACHINE_IS_SG1000) menu_debug_set_memory_zone(MEMORY_ZONE_SG1000_VRAM);
            if (MACHINE_IS_SMS) menu_debug_set_memory_zone(MEMORY_ZONE_SMS_VRAM);
			if (MACHINE_IS_SVI) menu_debug_set_memory_zone(MEMORY_ZONE_SVI_VRAM);

		}
	}

	else {
        //En caso de sms video mode 4, cambiamos los bpp a 4. Esto es solo identificativo para el usuario
        //porque realmente no lo lee cuando muestra los sprites en ese modo
        if (MACHINE_IS_SMS && view_sprites_sms_tiles) {
            //4 bpp en caso de modo 4 sms
            view_sprites_bpp=4;
            view_sprites_ppb=2;
        }

		view_sprites_bytes_por_linea=view_sprites_ancho_sprite/view_sprites_ppb;
		view_sprites_bytes_por_ventana=view_sprites_bytes_por_linea*view_sprites_alto_sprite;
		view_sprites_increment_cursor_vertical=view_sprites_bytes_por_linea;
	}
}

void menu_debug_view_sprites_textinfo(zxvision_window *ventana)
{
	int linea=0;
		char buffer_texto[64];

		//Antes de escribir, normalizar zona memoria
		menu_debug_set_memory_zone_attr();

		int restoancho=view_sprites_ancho_sprite % view_sprites_ppb;
		if (view_sprites_ancho_sprite-restoancho>0) view_sprites_ancho_sprite-=restoancho;

		//esto antes que menu_debug_sprites_get_parameters_hardware
		view_sprites_direccion=adjust_address_memory_size(view_sprites_direccion);

		menu_debug_sprites_get_parameters_hardware();



		char texto_memptr[33];

		if (view_sprites_hardware) {

			char texto_sprite[32];
			strcpy(texto_sprite,"Sprite");
			int max_sprites;
			if (MACHINE_IS_TSCONF) {
				max_sprites=TSCONF_MAX_SPRITES;
			}

			if (MACHINE_IS_TBBLUE) {
				if (view_sprites_bpp==4) max_sprites=TBBLUE_MAX_PATTERNS*2;
				else max_sprites=TBBLUE_MAX_PATTERNS;

				strcpy(texto_sprite,"Pattern");
			}

			if (MACHINE_HAS_VDP_9918A) {
				max_sprites=VDP_9918A_MAX_SPRITES;
                if (view_sprites_sms_tiles) max_sprites=VDP_9918A_SMS_MODE4_MAX_SPRITES;
			}
			sprintf(texto_memptr,"%s: %3d",texto_sprite,view_sprites_direccion%max_sprites); //dos digitos, tsconf hace 85 y tbblue hace 64. suficiente
		}

		else {
            char buffer_direccion[MAX_LENGTH_ADDRESS_MEMORY_ZONE+1];
            menu_debug_print_address_memory_zone(buffer_direccion,view_sprites_direccion);
			sprintf(texto_memptr,"Memptr:%s",buffer_direccion);
		}





		if (CPU_IS_MOTOROLA) sprintf (buffer_texto,"%s Size:%dX%d %dBPP",texto_memptr,view_sprites_ancho_sprite,view_sprites_alto_sprite,view_sprites_bpp);
		else sprintf (buffer_texto,"%s Size:%dX%d %dBPP",texto_memptr,view_sprites_ancho_sprite,view_sprites_alto_sprite,view_sprites_bpp);

		zxvision_print_string_defaults_fillspc(ventana,1,linea++,buffer_texto);

        //int alto_ventana=SPRITES_ALTO_VENTANA

		linea=(ventana->visible_height)-7;

    if (linea>=0) {

		char buffer_primera_linea[100]; //dar espacio de mas para poder alojar el ~de los atajos
		char buffer_segunda_linea[100];

		char buffer_tercera_linea[100];

		//Forzar a mostrar atajos
		z80_bit antes_menu_writing_inverse_color;
		antes_menu_writing_inverse_color.v=menu_writing_inverse_color.v;
		menu_writing_inverse_color.v=1;

		char nombre_paleta[33];
		menu_debug_sprites_get_palette_name(view_sprites_palette,nombre_paleta);

        //En el caso de usar modo sms sprites modo 4, paleta es siempre fija
        //Ademas indicar si offset es de Tiles o Sprites
        if (view_sprites_sms_tiles) {
            sprintf(buffer_tercera_linea,"Pa~~l.: SMS Mode 4. O~~ff:%d (%s)",view_sprites_offset_palette,
            (view_sprites_offset_palette == 0 ? "Tiles" : "Sprites"));
        }

		else sprintf(buffer_tercera_linea,"Pa~~l.: %s. O~~ff:%d",nombre_paleta,view_sprites_offset_palette);


		char mensaje_texto_hardware[64];

		//por defecto
		mensaje_texto_hardware[0]=0;

		if (MACHINE_IS_TSCONF || MACHINE_IS_TBBLUE || MACHINE_HAS_VDP_9918A) {
			sprintf(mensaje_texto_hardware,"[%c] ~~hardware",(view_sprites_hardware ? 'X' : ' ') );
		}

		char mensaje_texto_zx81_pseudohires[33];
		//por defecto
		mensaje_texto_zx81_pseudohires[0]=0;

		if (MACHINE_IS_ZX8081) {
			sprintf(mensaje_texto_zx81_pseudohires,"[%c] Ps~~eudohires",(view_sprites_zx81_pseudohires.v ? 'X' : ' ') );
		}

        char mensaje_texto_pcw_screen[33];
        //por defecto
        mensaje_texto_pcw_screen[0]=0;
        if (MACHINE_IS_PCW) {
            sprintf(mensaje_texto_pcw_screen,"[%c] PC~~W",(menu_sprites_modo_pcw_pantalla.v ? 'X' : ' '));
        }

		sprintf(buffer_primera_linea,"~~memptr In~~c+%d ~~o~~p~~q~~a:Size ~~bpp %s",
		view_sprite_incremento,
		(view_sprites_bpp==1 && !view_sprites_scr_sprite ? "~~save " : ""));

        char mensaje_texto_sms[32];
		//por defecto
		mensaje_texto_sms[0]=0;

        if (MACHINE_IS_SMS) {
            if (view_sprites_sms_tiles==0) sprintf(mensaje_texto_sms," [ ] SMS Mo~~de 4");

            //Con tipo hardware y habilitado view_sprites_sms_tiles, da igual el modo
            else if (view_sprites_hardware) sprintf(mensaje_texto_sms," [X] SMS Mo~~de 4");

            else sprintf(mensaje_texto_sms," [%c] SMS Mo~~de 4",(view_sprites_sms_tiles==1 ? '>' : 'v') );
        }

        char mensaje_magnify[30];
        mensaje_magnify[0]=0;
        if (!view_sprites_hardware) sprintf(mensaje_magnify,"[%d] Ma~~gnify",menu_debug_draw_sprites_zoom_sprites);

        char mensaje_grid[30];
        mensaje_grid[0]=0;
        if (menu_debug_draw_sprites_zoom_sprites>=4) sprintf(mensaje_grid,"[%c] ~^Grid",(menu_debug_draw_sprites_grid ? 'X' : ' '));

		sprintf(buffer_segunda_linea, "[%c] ~~inv [%c] Sc~~r %s%s%s%s %s %s",
					(view_sprites_inverse.v ? 'X' : ' '),
					(view_sprites_scr_sprite ? 'X' : ' '),
					mensaje_texto_hardware,mensaje_texto_sms,mensaje_texto_zx81_pseudohires,mensaje_texto_pcw_screen,
                    mensaje_magnify,mensaje_grid);


		zxvision_print_string_defaults_fillspc(ventana,1,linea++,buffer_primera_linea);
		zxvision_print_string_defaults_fillspc(ventana,1,linea++,buffer_segunda_linea);
		zxvision_print_string_defaults_fillspc(ventana,1,linea++,buffer_tercera_linea);

		//Mostrar zona memoria

		char textoshow[33];

		char memory_zone_text[MACHINE_MAX_MEMORY_ZONE_NAME_LENGHT+100];
		if (menu_debug_show_memory_zones==0) {
			sprintf (memory_zone_text,"Mem ~~zone (mapped memory)");
		}

		else {
			//printf ("Info zona %d\n",menu_debug_memory_zone);
			char buffer_name[MACHINE_MAX_MEMORY_ZONE_NAME_LENGHT+1];
			//int readwrite;
			machine_get_memory_zone_name(menu_debug_memory_zone,buffer_name);
			sprintf (memory_zone_text,"Mem ~~zone (%d %s)",menu_debug_memory_zone,buffer_name);
			//printf ("size: %X\n",menu_debug_memory_zone_size);
			//printf ("Despues zona %d\n",menu_debug_memory_zone);
		}



		zxvision_print_string_defaults_fillspc(ventana,1,linea++,memory_zone_text);

		sprintf (textoshow," Size: %d (%d KB)",menu_debug_memory_zone_size,menu_debug_memory_zone_size/1024);

		zxvision_print_string_defaults_fillspc(ventana,1,linea++,textoshow);



		//Restaurar comportamiento atajos
		menu_writing_inverse_color.v=antes_menu_writing_inverse_color.v;

    }


}


zxvision_window zxvision_window_view_sprites;

void menu_debug_view_sprites(MENU_ITEM_PARAMETERS)
{

    //Desactivamos interlace - si esta. Con interlace la forma de onda se dibuja encima continuamente, sin borrar
    //z80_bit copia_video_interlaced_mode;
    //copia_video_interlaced_mode.v=video_interlaced_mode.v;
	menu_espera_no_tecla();
	menu_reset_counters_tecla_repeticion();



    //disable_interlace();

	zxvision_window *ventana;
	ventana=&zxvision_window_view_sprites;

    //IMPORTANTE! no crear ventana si ya existe. Esto hay que hacerlo en todas las ventanas que permiten background.
    //si no se hiciera, se crearia la misma ventana, y en la lista de ventanas activas , al redibujarse,
    //la primera ventana repetida apuntaria a la segunda, que es el mismo puntero, y redibujaria la misma, y se quedaria en bucle colgado
	//zxvision_delete_window_if_exists(ventana);

	if (!MACHINE_IS_TBBLUE && !MACHINE_IS_TSCONF && !MACHINE_HAS_VDP_9918A) view_sprites_hardware=0;

	if (!MACHINE_IS_ZX8081) view_sprites_zx81_pseudohires.v=0;

    //Crear ventana si no existe
    if (!zxvision_if_window_already_exists(ventana)) {

        int x,y,ancho,alto,is_minimized,is_maximized,ancho_antes_minimize,alto_antes_minimize;


        if (!util_find_window_geometry("sprites",&x,&y,&ancho,&alto,&is_minimized,&is_maximized,&ancho_antes_minimize,&alto_antes_minimize)) {
            //x=SPRITES_X;
            //y=SPRITES_Y;
            ancho=SPRITES_ANCHO;
            alto=SPRITES_ALTO_VENTANA;

            x=menu_center_x()-ancho/2;
            y=menu_center_y()-alto/2;
        }


        //zxvision_new_window_nocheck_staticsize(ventana,x,y,ancho,alto,64,64+2,"Sprites");
        zxvision_new_window_gn_cim(ventana,x,y,ancho,alto,1024/menu_char_width,(1024/menu_char_height)+2,"Sprites","sprites",is_minimized,is_maximized,ancho_antes_minimize,alto_antes_minimize);


        ventana->can_be_backgrounded=1;
        //indicar nombre del grabado de geometria
        //strcpy(ventana->geometry_name,"sprites");
        //restaurar estado minimizado de ventana
        //ventana->is_minimized=is_minimized;

        //Permitir hotkeys desde raton
        ventana->can_mouse_send_hotkeys=1;

        //decimos que tiene que borrar fondo cada vez al redibujar
        //por tanto es como decirle que no use cache de putchar
        //dado que el fondo de texto es casi todo texto con caracter " " eso borra los pixeles que metemos con overlay del frame anterior
        //ventana->must_clear_cache_on_draw=1;
    }

    //Si ya existe, activar esta ventana
    else {

        zxvision_activate_this_window(ventana);
    }

	zxvision_draw_window(ventana);

	z80_byte tecla;

    menu_debug_draw_sprites_window=ventana; //Decimos que el overlay lo hace sobre la ventana que tenemos aqui

    //Cambiamos funcion overlay de texto de menu
    //Se establece a la de funcion de ver sprites

    //cambio overlay
    zxvision_set_window_overlay(ventana,menu_debug_draw_sprites);




    //Toda ventana que este listada en zxvision_known_window_names_array debe permitir poder salir desde aqui
    //Se sale despues de haber inicializado overlay y de cualquier otra variable que necesite el overlay
    if (zxvision_currently_restoring_windows_on_start) {
            //printf ("Saliendo de ventana ya que la estamos restaurando en startup\n");
            return;
    }

	int redibujar_texto=1;

    int ancho_anterior,alto_anterior;
    zxvision_window_save_size(ventana,&ancho_anterior,&alto_anterior);

    do {


		//Solo redibujar el texto cuando alguna tecla pulsada sea de las validas,
		//y no con mouse moviendo la ventana
		//porque usa mucha cpu y por ejemplo en maquina tsconf se clava si arrastramos ventana
		if (redibujar_texto) {
			//printf ("redibujamos texto\n");


			menu_debug_view_sprites_textinfo(ventana);


			//Si no esta multitarea, mostrar el texto que acabamos de escribir
	    	if (!menu_multitarea) {
				zxvision_draw_window_contents(ventana);
			}
		}


		tecla=zxvision_common_getkey_refresh();
		//printf ("tecla: %d\n",tecla);

		if (tecla) redibujar_texto=1;

        switch (tecla) {

					case 8:
						//izquierda
						view_sprites_direccion--;
					break;

					case 9:
						//derecha
						view_sprites_direccion++;
					break;

                    case 11:
                        //arriba
                        view_sprites_direccion -=view_sprites_increment_cursor_vertical;
                    break;

                    case 10:
                        //abajo
                        view_sprites_direccion +=view_sprites_increment_cursor_vertical;
                    break;

                    case 24:
                        //PgUp
                        view_sprites_direccion -=view_sprites_bytes_por_ventana;
                    break;

                    case 25:
                        //PgDn
                        view_sprites_direccion +=view_sprites_bytes_por_ventana;
                    break;

                    case 'm':
                        view_sprites_direccion=menu_debug_view_sprites_change_pointer(view_sprites_direccion);
							zxvision_draw_window(ventana);
                    break;

					case 'b':
						menu_debug_sprites_change_bpp();
					break;

					case 'd':
                        if (MACHINE_IS_SMS) {
                            view_sprites_sms_tiles++;
                            //En modo hardware solo tendra dos valores, 0 y 1
                            if (view_sprites_hardware) {
                                if (view_sprites_sms_tiles>=2) view_sprites_sms_tiles=0;
                            }

                            //Y en modo no hardware, se permiten valores 0, 1 y 2
                            else {
                                if (view_sprites_sms_tiles==3) view_sprites_sms_tiles=0;
                            }
                        }
					break;

					case 'f':
                        //En modo sms solo permitimos dos offset distintos, 0 y 16
                        if (view_sprites_sms_tiles) {
                            view_sprites_offset_palette +=16;
                            if (view_sprites_offset_palette>16) view_sprites_offset_palette=0;
                        }
                        else {
						    view_sprites_offset_palette++;

						    if (view_sprites_offset_palette>=256) view_sprites_offset_palette=0;
                        }
					break;

                    case 'g':
                        menu_debug_draw_sprites_zoom_sprites++;
                        if (menu_debug_draw_sprites_zoom_sprites>8) {
                            menu_debug_draw_sprites_zoom_sprites=1;
                            ventana->must_clear_cache_on_draw_once=1;
                        }
                    break;

                    case 'G':
                        menu_debug_draw_sprites_grid ^=1;
                    break;


					case 'l':
						menu_debug_sprites_change_palette();
					break;

					case 'h':
						if (MACHINE_IS_TBBLUE || MACHINE_IS_TSCONF || MACHINE_HAS_VDP_9918A) {
                            view_sprites_hardware ^=1;
                            //Otra alternativa de borrar el fondo. En vez de tener esta variable must_clear_cache_on_draw=1 siempre,
                            //solo la alteramos momentaneamente al cambiar tipo de sprite hardware, con esto se borra correctamente y en cambio
                            //el uso de cpu cuando no modificamos pasa por ejemplo de un uso de 82% teniendo esto siempre a 1,
                            //a usar 52% cuando lo tenemos a 0
                            ventana->must_clear_cache_on_draw_once=1;
                        }
					break;

					case 'e':
						if (MACHINE_IS_ZX8081) view_sprites_zx81_pseudohires.v ^=1;
					break;

					case 'i':
						view_sprites_inverse.v ^=1;
					break;

                    case 'w':
                        menu_sprites_modo_pcw_pantalla.v ^=1;
                    break;

					case 'z':


						menu_debug_change_memory_zone();



						break;

					case 's':


						if (view_sprites_hardware) {

						}

						else {

							//Solo graba sprites de 1bpp (monocromos)
							if (view_sprites_bpp==1 && !view_sprites_scr_sprite) {


								if (menu_debug_view_sprites_save(view_sprites_direccion,view_sprites_ancho_sprite,view_sprites_alto_sprite,view_sprites_ppb,view_sprite_incremento)) {
									menu_error_message("Unknown file format");
								}


								zxvision_draw_window(ventana);
							}

						}
					break;

					case 'o':
						if (view_sprites_ancho_sprite>view_sprites_ppb) {
                            view_sprites_ancho_sprite -=view_sprites_ppb;

                            //Otra alternativa de borrar el fondo. En vez de tener esta variable must_clear_cache_on_draw=1 siempre,
                            //solo la alteramos momentaneamente al reducir sprite, con esto se borra correctamente y en cambio
                            //el uso de cpu cuando no modificamos pasa por ejemplo de un uso de 82% teniendo esto siempre a 1,
                            //a usar 52% cuando lo tenemos a 0
                            ventana->must_clear_cache_on_draw_once=1;
                        }
					break;

					case 'p':

						if (view_sprites_ancho_sprite<1024) view_sprites_ancho_sprite +=view_sprites_ppb;
					break;

                    case 'q':
                        if (view_sprites_alto_sprite>1) {
                            view_sprites_alto_sprite--;
                            ventana->must_clear_cache_on_draw_once=1;
                        }
                    break;

                    case 'a':
						if (view_sprites_alto_sprite<1024)  view_sprites_alto_sprite++;
                    break;

					case 'c':
							if (view_sprite_incremento==1) view_sprite_incremento=2;
							else view_sprite_incremento=1;
					break;

					case 'r':
							view_sprites_scr_sprite ^=1;
					break;

					default:
						//no es tecla valida, no redibujar texto
						redibujar_texto=0;
					break;

		}

		if (ventana->visible_height!=alto_anterior || ventana->visible_width!=ancho_anterior) {
            //printf("Reescribir texto porque ha cambiado tamanyo ventana\n");

			redibujar_texto=1;

            zxvision_window_save_size(ventana,&ancho_anterior,&alto_anterior);

            zxvision_cls(ventana);

		}


    } while (tecla!=2 && tecla!=3);







    	//Grabar geometria ventana

		util_add_window_geometry_compact(ventana);


	if (tecla==3) {
		zxvision_message_put_window_background();
	}

	else {



		zxvision_destroy_window(ventana);
	}



}



/*

Partitura



 --------------------------------

 --------------------------------

 --------------------------------

 --------------------------------

 --------------------------------



5 lineas de pentagrana -> 4 separaciones

8 pixeles de alto cada separacion -> 32 pixeles cada pentagrama

Si cogemos dos mas para subir hasta el Do, son 8x6=48 pixeles por pentagama

48 * 3 = 144 los 3 pentagramas


Dibujo de la nota:

     012345678901
0 -----------------------------------
1    ..XXX..
2    .X...X.
3    X.....X
4    X.....X
5    X.....X
6    .X...X.
7    ..XXX..
8 ----------------------------------
*/

#define PENTAGRAMA_NOTA_ALTO 7
#define PENTAGRAMA_NOTA_ANCHO 7
#define PENTAGRAMA_NOTA_LARGO_PALITO 17

//donde empieza el palito
#define PENTAGRAMA_NOTA_OFFSET_PALITO 3

#define PENTAGRAMA_ESPACIO_LINEAS 8

#define PENTAGRAMA_SOST_ANCHO 8
#define PENTAGRAMA_SOST_ALTO 11

#define PENTAGRAMA_ANCHO_NOTA_TOTAL (PENTAGRAMA_SOST_ANCHO+PENTAGRAMA_NOTA_ANCHO+6)

#define PENTAGRAMA_MARGEN_SOSTENIDO (PENTAGRAMA_SOST_ANCHO+2)

#define PENTAGRAMA_TOTAL_ALTO (PENTAGRAMA_ESPACIO_LINEAS*7)

//#define PENTAGRAMA_MARGEN_SUPERIOR 8
//Si hay mas de un chip, meter margen superior
#define PENTAGRAMA_MARGEN_SUPERIOR (total_ay_chips>1 ? 8 : 0)

char *pentagrama_nota_negra[PENTAGRAMA_NOTA_ALTO]={
   //0123456
    "  XXX  ",
	" XXXXX ",
	"XXXXXXX",
	"XXXXXXX",
	"XXXXXXX",
	" XXXXX ",
	"  XXX  "
};

char *pentagrama_nota_blanca[PENTAGRAMA_NOTA_ALTO]={
   //0123456
    "  XXX  ",
	" X   X ",
	"X     X",
	"X     X",
	"X     X",
	" X   X ",
	"  XXX  "
};

#define PENTAGRAMA_PUNTILLO_ALTO 2
#define PENTAGRAMA_PUNTILLO_ANCHO 2

/* char *pentagrama_puntillo[PENTAGRAMA_PUNTILLO_ALTO]={
   //0123456
    " X ",
	"XXX",
	" X "
};*/

char *pentagrama_puntillo[PENTAGRAMA_PUNTILLO_ALTO]={
   //0123456
    "XX",
	"XX",
};


#define PENTAGRAMA_CLAVE_SOL_ALTO 44
#define PENTAGRAMA_CLAVE_SOL_ANCHO 30

char *pentagrama_clave_sol[PENTAGRAMA_CLAVE_SOL_ALTO]={
/*
 012345678901234567890123456789012345
*/

"                 XXXXX        ",
"               XXXXXXXX       ",
"              XXXXXXXXXX      ",
"             XXXXXXXXXXX      ",
"            XXXXX     XX      ",
"            XXXX      XX      ",
"            XXX       XX      ",
"            XX        XX      ",
"            XX      XXXX      ",
"            XX     XXXXX      ",
"            XX     XXXXX      ",
"            XX  XXXXXXX       ",
"            XXXXXXXXX         ",
"            XXXXXXXXX         ",
"          XXXXXXXXXX          ",
"        XXXXXXXXXX            ",
"       XXXXXXXXX              ",
"     XXXXXXXXXXX              ",
"    XXXXXXXXXX XX             ",
"   XXXXXXXXX   XX             ",
"  XXXXXXXX     XXXXXXX        ",
"  XXXXXXX    XXXXXXXXXXXX     ",
" XXXXX     XXXXXXXXXXXXXXXXX  ",
"XXXXX     XXXXXXXXXXXXXXXXXXX ",
"XXXXXX   XXXXXX   XX   XXXXXXX",
"XXXXXX   XXXXX     XX    XXXXX",
" XXXXX   XXXXX      XX     XXX",
"  XXXX    XXXX      XX     XXX",
"  XXXXX   XXXXX     XX     XX ",
"   XXXXX   XXXX      XX  XXXX ",
"    XXXXXX           XX XXXX  ",
"     XXXXXXX         XXXXX    ",
"          XXXXXXXXXXXXXXX     ",
"             XXXXXXXXXXX      ",
"                      XX      ",
"                      XX      ",
"          XXX         XX      ",
"        XXXXXX        XX      ",
"       XXXXXXXX       XX      ",
"       XXXXXXXX       XX      ",
"        XXXXXX       XX       ",
"          XXX       XX        ",
"          XXXXXXXXXXX          ",
"            XXXXXXXX          ",
};


char *pentagrama_sost[PENTAGRAMA_SOST_ALTO]={

//0123456789012
 "  X  X  ",    //0
 "  X  X  ",
 "  X  XXX",
 "  XXXX  ",
 "XXX  X  ",
 "  X  X  ",   //5
 "  X  XXX",
 "  XXXX  ",
 "XXX  X  ",
 "  X  X  ",
 "  X  X  "     //10

};


//Clave de sol. 56 pixeles alto aprox

//#define PIANO_PARTITURA_GRAPHIC_BASE_X (menu_origin_x() )
//#define PIANO_PARTITURA_GRAPHIC_BASE_Y 0



#define PIANO_PARTITURA_ANCHO_VENTANA 32
#define PIANO_PARTITURA_ALTO_VENTANA 24

#define MENU_AY_PARTITURA_MAX_COLUMNS 30

zxvision_window *menu_ay_partitura_overlay_window;


//Lo que contiene cada pentagrama, de cada chip, de cada canal

//Chip, canal, columna, string de 4
char menu_ay_partitura_current_state[MAX_AY_CHIPS][3][MENU_AY_PARTITURA_MAX_COLUMNS][4];

//Chip, canal, columna, duracion de cada nota
int menu_ay_partitura_current_state_duraciones[MAX_AY_CHIPS][3][MENU_AY_PARTITURA_MAX_COLUMNS];

//Ultima columna de cada canal usada
int menu_ay_partitura_ultima_columna[3];

//Nota anterior de la ultima columna
//char menu_ay_partitura_last_state[MAX_AY_CHIPS][3][4];


//Hacer putpixel en pantalla de color indexado 16 bits. Usado en watermark para no rainbow
void menu_ay_partitura_putpixel_nota(z80_int *destino GCC_UNUSED,int x,int y,int ancho_destino GCC_UNUSED,int color GCC_UNUSED)
{
	//scr_putpixel(x,y,color);

	//zxvision_putpixel(menu_ay_partitura_overlay_window,x,y,color);
	zxvision_putpixel(menu_ay_partitura_overlay_window,x,y,ESTILO_GUI_TINTA_NORMAL);
}

void menu_ay_partitura_dibujar_sost(int x,int y)
{
	screen_put_asciibitmap_generic(pentagrama_sost,NULL,x,y,PENTAGRAMA_SOST_ANCHO,PENTAGRAMA_SOST_ALTO,0,menu_ay_partitura_putpixel_nota,1,0);
}

//duraciones notas
enum aysheet_tipo_nota_duracion {
	AYSHEET_NOTA_SEMIFUSA,
	AYSHEET_NOTA_SEMIFUSA_PUNTO,

	AYSHEET_NOTA_FUSA,
	AYSHEET_NOTA_FUSA_PUNTO,

	AYSHEET_NOTA_SEMICORCHEA,
	AYSHEET_NOTA_SEMICORCHEA_PUNTO,

	AYSHEET_NOTA_CORCHEA,
	AYSHEET_NOTA_CORCHEA_PUNTO,

	AYSHEET_NOTA_NEGRA,
	AYSHEET_NOTA_NEGRA_PUNTO,

	AYSHEET_NOTA_BLANCA,
	AYSHEET_NOTA_BLANCA_PUNTO,

	AYSHEET_NOTA_REDONDA,
	AYSHEET_NOTA_REDONDA_PUNTO

};

//retorne el char * de donde esta la nota (blanca,redonda=pentagrama_nota_blanca. resto=pentagrama_nota_negra)
char **aysheet_tipo_nota_bitmap(enum aysheet_tipo_nota_duracion nota)
{
	switch (nota) {
		case AYSHEET_NOTA_BLANCA:
		case AYSHEET_NOTA_BLANCA_PUNTO:
		case AYSHEET_NOTA_REDONDA:
		case AYSHEET_NOTA_REDONDA_PUNTO:
			return pentagrama_nota_blanca;
		break;

		default:
			return pentagrama_nota_negra;
		break;
	}
}


//dice si nota tiene "palito", o sea, todos menos la redonda
int aysheet_tipo_nota_tienepalo(enum aysheet_tipo_nota_duracion nota)
{
	switch (nota) {
		case AYSHEET_NOTA_REDONDA:
		case AYSHEET_NOTA_REDONDA_PUNTO:
			return 0;
		break;

		default:
			return 1;
		break;
	}
}

//dice el numero de diagonales que tiene la nota (corchea, semicorchea, fusa, semifusa)
int aysheet_tipo_nota_diagonales(enum aysheet_tipo_nota_duracion nota)
{
	switch (nota) {
		case AYSHEET_NOTA_CORCHEA:
		case AYSHEET_NOTA_CORCHEA_PUNTO:
			return 1;
		break;

		case AYSHEET_NOTA_SEMICORCHEA:
		case AYSHEET_NOTA_SEMICORCHEA_PUNTO:
			return 2;
		break;

		case AYSHEET_NOTA_FUSA:
		case AYSHEET_NOTA_FUSA_PUNTO:
			return 3;
		break;

		case AYSHEET_NOTA_SEMIFUSA:
		case AYSHEET_NOTA_SEMIFUSA_PUNTO:
			return 4;
		break;


		default:
			return 0;
		break;
	}
}

//dice si nota tiene puntillo
int aysheet_tipo_nota_tienepuntillo(enum aysheet_tipo_nota_duracion nota)
{
	switch (nota) {
		case AYSHEET_NOTA_SEMIFUSA_PUNTO:
		case AYSHEET_NOTA_FUSA_PUNTO:
		case AYSHEET_NOTA_SEMICORCHEA_PUNTO:
		case AYSHEET_NOTA_CORCHEA_PUNTO:
		case AYSHEET_NOTA_NEGRA_PUNTO:
		case AYSHEET_NOTA_BLANCA_PUNTO:
		case AYSHEET_NOTA_REDONDA_PUNTO:
			return 1;
		break;

		default:
			return 0;
		break;
	}
}

//Devuelve tipo de nota segun su duracion en 1/50 de segundo
enum aysheet_tipo_nota_duracion menu_aysheet_get_length(int duracion)
{
/*
	Duraciones notas:


	3.125=0.0625 segundos=semifusa
	4.6875=0.09375 segundos=semifusa con punto

	6.25=0.125 segundos=fusa
	9.375=0.1875 segundos=fusa con punto

	12.5=0.25 segundos=semicorchea
	18.75=0.375 segundos=semicorchea con punto

	25=0.5 segundos=corchea
	37.5=0.75 segundos=corchea con punto

	50=1 segundo=negra
	75=1.5 segundos=negra con punto

	100=2 segundos=blanca
	150=3 segundos=blanca con punto

	200=4 segundos=redonda
	300=6 segundos=redonda con punto

	 */

	//Vemos duraciones segun si es menor o igual. Hacemos redondeos de duraciones: 4.6 es 5
	if (duracion<=3) return AYSHEET_NOTA_SEMIFUSA;
	if (duracion<=5) return AYSHEET_NOTA_SEMIFUSA_PUNTO;
	if (duracion<=6) return AYSHEET_NOTA_FUSA;
	if (duracion<=9) return AYSHEET_NOTA_FUSA_PUNTO;

	if (duracion<=12) return AYSHEET_NOTA_SEMICORCHEA;
	if (duracion<=19) return AYSHEET_NOTA_SEMICORCHEA_PUNTO;
	if (duracion<=25) return AYSHEET_NOTA_CORCHEA;
	if (duracion<=37) return AYSHEET_NOTA_CORCHEA_PUNTO;

	if (duracion<=50) return AYSHEET_NOTA_NEGRA;
	if (duracion<=75) return AYSHEET_NOTA_NEGRA_PUNTO;
	if (duracion<=100) return AYSHEET_NOTA_BLANCA;
	if (duracion<=150) return AYSHEET_NOTA_BLANCA_PUNTO;

	if (duracion<=200) return AYSHEET_NOTA_REDONDA;

	//Cualquier otra cosa
	return AYSHEET_NOTA_REDONDA_PUNTO;

}

//incremento_palito: +1 : palito hacia abajo
//incremento_palito: +1 : palito hacia arriba
//duracion en 1/50 de segundos. 50=negra
void menu_ay_partitura_dibujar_nota(int x,int y,int incremento_palito,int duracion)
{


	enum aysheet_tipo_nota_duracion tipo_nota_duracion=menu_aysheet_get_length(duracion);

	char **bitmap_nota=aysheet_tipo_nota_bitmap(tipo_nota_duracion);

	bitmap_nota=aysheet_tipo_nota_bitmap(tipo_nota_duracion);




	screen_put_asciibitmap_generic(bitmap_nota,NULL,x,y,PENTAGRAMA_NOTA_ANCHO,PENTAGRAMA_NOTA_ALTO,0,menu_ay_partitura_putpixel_nota,1,0);


	//PENTAGRAMA_NOTA_LARGO_PALITO
	if (aysheet_tipo_nota_tienepalo(tipo_nota_duracion)) {
		int yorig=y+PENTAGRAMA_NOTA_OFFSET_PALITO;

		int xorig=x;

		//Si palito hacia arriba
		if (incremento_palito<0) xorig=x+PENTAGRAMA_NOTA_ANCHO-1;

		int alto=PENTAGRAMA_NOTA_LARGO_PALITO;

		for (;alto>0;alto--,yorig +=incremento_palito) {
			zxvision_putpixel(menu_ay_partitura_overlay_window,xorig,yorig,ESTILO_GUI_TINTA_NORMAL);
		}


		//Diagonales de la nota. corchea, semi, etc
		int diagonales=aysheet_tipo_nota_diagonales(tipo_nota_duracion);
		int i;
		int largo_diagonal=5;


		for (i=0;i<diagonales;i++) {
			int l;
			yorig=y+PENTAGRAMA_NOTA_OFFSET_PALITO+((PENTAGRAMA_NOTA_LARGO_PALITO-i*3)*incremento_palito);
			for (l=0;l<largo_diagonal;l++) {
				zxvision_putpixel(menu_ay_partitura_overlay_window,xorig+l,yorig-(l*incremento_palito),ESTILO_GUI_TINTA_NORMAL);
			}
		}
	}

	//Si hay que dibujar puntillo
	if (aysheet_tipo_nota_tienepuntillo(tipo_nota_duracion)) {
		screen_put_asciibitmap_generic(pentagrama_puntillo,NULL,x+PENTAGRAMA_NOTA_ANCHO+1,y+PENTAGRAMA_NOTA_ALTO/2+1,
				PENTAGRAMA_PUNTILLO_ANCHO,PENTAGRAMA_PUNTILLO_ALTO,0,menu_ay_partitura_putpixel_nota,1,0);
	}




}

void meny_ay_partitura_dibujar_clavesol(int x,int y)
{
	screen_put_asciibitmap_generic(pentagrama_clave_sol,NULL,x,y,PENTAGRAMA_CLAVE_SOL_ANCHO,PENTAGRAMA_CLAVE_SOL_ALTO,0,menu_ay_partitura_putpixel_nota,1,0);
}




void menu_ay_partitura_linea(int x,int y,int ancho)
{

		for (;ancho>0;ancho--,x++) {
			zxvision_putpixel(menu_ay_partitura_overlay_window,x,y,ESTILO_GUI_TINTA_NORMAL);
		}
}

void menu_ay_partitura_lineas_pentagrama(int x,int y,int ancho,int separacion_alto)
{
	int lineas;

	int y_clavesol=y-7;

	for (lineas=0;lineas<5;lineas++) {
		menu_ay_partitura_linea(x,y,ancho);

		y +=separacion_alto;
	}


	meny_ay_partitura_dibujar_clavesol(x,y_clavesol);
}

//nota puede ser:  do, re, mi, fa, sol, la, si, do, re... si (0...13)
void menu_ay_partitura_nota_pentagrama(int x,int y,int nota,int si_sostenido,int duracion)
{
	//origen y=fa (10)

	int diferencia_nota=10-nota;

	int incremento_alto=diferencia_nota*(PENTAGRAMA_ESPACIO_LINEAS/2);

	//Y el punto inicial y
	int ynota=y+incremento_alto-PENTAGRAMA_NOTA_OFFSET_PALITO;

	//A partir del do, palito para abajo
	int incremento_palito=-1;

	if (nota>=6) incremento_palito=+1; //A partir del Si , palito para abajo


	menu_ay_partitura_dibujar_nota(x,ynota,incremento_palito,duracion);

	//Si hay que poner palito de linea pentagrama (en do (0), la (12), si(12))
	if (nota==0 || nota==12 || nota==13) {
		int ypalito;
		if (nota==0 || nota==12) ypalito=ynota+PENTAGRAMA_NOTA_OFFSET_PALITO;
		else ypalito=ynota+PENTAGRAMA_NOTA_ALTO; //si (12)

		menu_ay_partitura_linea(x-2,ypalito,PENTAGRAMA_NOTA_ANCHO+4);
	}

	if (si_sostenido) {
		x=x-PENTAGRAMA_MARGEN_SOSTENIDO;
		ynota -=2; //un poquito mas para arriba
		menu_ay_partitura_dibujar_sost(x,ynota);
	}


}

//nota puede ser:  do, re, mi, fa, sol, la, si, do, re... si (0...13)
void menu_ay_partitura_nota_pentagrama_pos(int xorig,int yorig,int columna,int nota,int si_sostenido,int duracion)
{
	int ancho_columna=PENTAGRAMA_ANCHO_NOTA_TOTAL;

	int posx=(columna*ancho_columna)+xorig;

	//Y darle margen por la izquierda pos si hay sostenido
	posx +=PENTAGRAMA_MARGEN_SOSTENIDO;

	//darle margen de la clave de sol
	posx +=PENTAGRAMA_CLAVE_SOL_ANCHO;

	menu_ay_partitura_nota_pentagrama(posx,yorig,nota,si_sostenido,duracion);
}



int menu_ay_partitura_ancho_col_texto(void)
{
	return menu_char_width;
}

int menu_ay_partitura_total_columns(void)
{
	int ancho_columna=menu_ay_partitura_ancho_col_texto();
	int ancho_nota=PENTAGRAMA_ANCHO_NOTA_TOTAL;
	int total_columnas=(((menu_ay_partitura_overlay_window->visible_width)*ancho_columna)-PENTAGRAMA_MARGEN_SOSTENIDO*2)/ancho_nota;

	total_columnas--; //1 menos

	//de la clave de sol
	total_columnas--;


	if (total_columnas>MENU_AY_PARTITURA_MAX_COLUMNS) total_columnas=MENU_AY_PARTITURA_MAX_COLUMNS;

	//control minimos
	if (total_columnas<2) total_columnas=2;

	return total_columnas;
}




//Scroll de un chip entero
void menu_ay_partitura_scroll(int chip)
{

		//Meter valor actual

	int total_columnas=menu_ay_partitura_total_columns();
	int i;

	for (i=0;i<total_columnas-1;i++) {
		int canal;
		for (canal=0;canal<3;canal++) {
			strcpy(menu_ay_partitura_current_state[chip][canal][i],menu_ay_partitura_current_state[chip][canal][i+1]);
			menu_ay_partitura_current_state_duraciones[chip][canal][i]=menu_ay_partitura_current_state_duraciones[chip][canal][i+1];
		}
	}

	//La ultima columna ponerla a vacia
		int canal;
		for (canal=0;canal<3;canal++) {
			menu_ay_partitura_current_state[chip][canal][i][0]=0;
			menu_ay_partitura_current_state_duraciones[chip][canal][i]=0;
		}

	//Desplazar indices de ultima columna a la izquierda

	for (i=0;i<3;i++) {
		int c=menu_ay_partitura_ultima_columna[i];
		if (c>0) {
			menu_ay_partitura_ultima_columna[i]=c-1;
		}
	}
}





void menu_ay_partitura_draw_state(int chip,int canal)
{

	int x=0;
	int y=PENTAGRAMA_ESPACIO_LINEAS*2;
	int duracion;


	y +=canal*(PENTAGRAMA_TOTAL_ALTO+1); //+1 para dejar 1 pixelillo de margen


	y +=PENTAGRAMA_MARGEN_SUPERIOR;


	int ancho_columna=menu_ay_partitura_ancho_col_texto();

	//Las lineas de pentagrama que dejen espacio a la izquierda y derecha, de ancho=ancho_columna
	menu_ay_partitura_lineas_pentagrama(x+ancho_columna,y,((menu_ay_partitura_overlay_window->visible_width)-2)*ancho_columna,PENTAGRAMA_ESPACIO_LINEAS);



	int ancho_nota=PENTAGRAMA_ANCHO_NOTA_TOTAL;
	int total_columnas;


	int i;


	total_columnas=menu_ay_partitura_total_columns();
	//printf ("total columnas: %d\n",total_columnas);

	for (i=0;i<total_columnas;i++) {
		char *string_nota;
		string_nota=menu_ay_partitura_current_state[chip][canal][i];
		//if (canal==0) printf ("%d [%s]\n",i,string_nota);

		//Nota leida canal 0

		int nota_final=-1;
		int octava;
		int sostenido;

		get_note_values(string_nota,&nota_final,&sostenido,&octava);
		if (nota_final>=0) {

			//Si octava impar, va hacia arriba
			if (octava & 1) nota_final +=7;

			duracion=menu_ay_partitura_current_state_duraciones[chip][canal][i];

			menu_ay_partitura_nota_pentagrama_pos(x+ancho_nota,y,i,nota_final,sostenido,duracion);
		}
	}



}

int menu_ay_partitura_chip=0;

void menu_ay_partitura_overlay(void)
{




	menu_speech_set_tecla_pulsada(); //Si no, envia continuamente todo ese texto a speech, en el caso que se habilite piano de tipo texto


    //si ventana minimizada, no ejecutar todo el codigo de overlay
    if (menu_ay_partitura_overlay_window->is_minimized) return;

    //printf("Overlay ay sheet %d\n",contador_segundo);



    char nota_a[4];
    char nota_b[4];
    char nota_c[4];


    int freq_a,freq_b,freq_c;

    freq_a=audio_retorna_frecuencia_canal(0,menu_ay_partitura_chip);
    freq_b=audio_retorna_frecuencia_canal(1,menu_ay_partitura_chip);
    freq_c=audio_retorna_frecuencia_canal(2,menu_ay_partitura_chip);




    sprintf(nota_a,"%s",get_note_name(freq_a) );


    sprintf(nota_b,"%s",get_note_name(freq_b) );


    sprintf(nota_c,"%s",get_note_name(freq_c) );

    //Si canales no suenan como tono, o volumen 0 meter cadena vacia en nota
    if (!audio_si_canal_tono(menu_ay_partitura_chip,0)) {
        nota_a[0]=0;
    }
    if (!audio_si_canal_tono(menu_ay_partitura_chip,1)) {
        nota_b[0]=0;
    }
    if (!audio_si_canal_tono(menu_ay_partitura_chip,2)) {
        nota_c[0]=0;
    }





	//Si notas anteriores distintas de las actuales, scroll izquierda


	//printf ("a [%s] [%s]\n",nota_a,menu_ay_partitura_last_state[0][0]);
	//printf ("b [%s] [%s]\n",nota_b,menu_ay_partitura_last_state[0][1]);
	//printf ("c [%s] [%s]\n",nota_c,menu_ay_partitura_last_state[0][2]);

	int columna_estado_anterior;
	//columna_estado_anterior=menu_ay_partitura_total_columns()-1;

	int hayscroll=0;
	int modificado_canal1=0;
	int modificado_canal2=0;
	int modificado_canal3=0;

	//Si alguno de los 3 canales es diferente del estado anterior
	columna_estado_anterior=menu_ay_partitura_ultima_columna[0];
	if (strcasecmp(nota_a,menu_ay_partitura_current_state[menu_ay_partitura_chip][0][columna_estado_anterior])) {
		hayscroll=1;
		modificado_canal1=1;
	}
	else {
		//se mantiene igual. aumentar duracion
		menu_ay_partitura_current_state_duraciones[menu_ay_partitura_chip][0][columna_estado_anterior]++;
	}


	columna_estado_anterior=menu_ay_partitura_ultima_columna[1];
	if (strcasecmp(nota_b,menu_ay_partitura_current_state[menu_ay_partitura_chip][1][columna_estado_anterior])) {
		hayscroll=1;
		modificado_canal2=1;
	}
	else {
		//se mantiene igual. aumentar duracion
		menu_ay_partitura_current_state_duraciones[menu_ay_partitura_chip][1][columna_estado_anterior]++;
	}


	columna_estado_anterior=menu_ay_partitura_ultima_columna[2];
	if (strcasecmp(nota_c,menu_ay_partitura_current_state[menu_ay_partitura_chip][2][columna_estado_anterior])) {
		hayscroll=1;
		modificado_canal3=1;
	}
	else {
		//se mantiene igual. aumentar duracion
		menu_ay_partitura_current_state_duraciones[menu_ay_partitura_chip][2][columna_estado_anterior]++;
	}




	if (hayscroll) {
		menu_ay_partitura_scroll(menu_ay_partitura_chip);

		//Meter valor actual los que se han modificado
		if (modificado_canal1) {
			//Y decir que ultima columna es la de mas a la derecha
			columna_estado_anterior=menu_ay_partitura_total_columns()-1;
			menu_ay_partitura_ultima_columna[0]=columna_estado_anterior;

			strcpy(menu_ay_partitura_current_state[menu_ay_partitura_chip][0][columna_estado_anterior],nota_a);
			menu_ay_partitura_current_state_duraciones[menu_ay_partitura_chip][0][columna_estado_anterior]=1;
		}

		if (modificado_canal2) {
			//Y decir que ultima columna es la de mas a la derecha
			columna_estado_anterior=menu_ay_partitura_total_columns()-1;
			menu_ay_partitura_ultima_columna[1]=columna_estado_anterior;

			strcpy(menu_ay_partitura_current_state[menu_ay_partitura_chip][1][columna_estado_anterior],nota_b);
			menu_ay_partitura_current_state_duraciones[menu_ay_partitura_chip][1][columna_estado_anterior]=1;
		}

		if (modificado_canal3) {
			//Y decir que ultima columna es la de mas a la derecha
			columna_estado_anterior=menu_ay_partitura_total_columns()-1;
			menu_ay_partitura_ultima_columna[2]=columna_estado_anterior;

			strcpy(menu_ay_partitura_current_state[menu_ay_partitura_chip][2][columna_estado_anterior],nota_c);
			menu_ay_partitura_current_state_duraciones[menu_ay_partitura_chip][2][columna_estado_anterior]=1;
		}



	}

	//Dibujar estado de los 3 canales
	menu_ay_partitura_draw_state(menu_ay_partitura_chip,0);
	menu_ay_partitura_draw_state(menu_ay_partitura_chip,1);
	menu_ay_partitura_draw_state(menu_ay_partitura_chip,2);




	zxvision_draw_window_contents(menu_ay_partitura_overlay_window);

}


void menu_ay_partitura_init_state(void)
{


    //Inicializar estado con string "" y duraciones 0



    int chip;
    for (chip=0;chip<MAX_AY_CHIPS;chip++) {
        int canal;
        for (canal=0;canal<3;canal++) {
            int col;

            for (col=0;col<MENU_AY_PARTITURA_MAX_COLUMNS;col++) {
                menu_ay_partitura_current_state[chip][canal][col][0]=0;
                menu_ay_partitura_current_state_duraciones[chip][canal][col]=0;

            }
        }
    }


}

void menu_ay_partitura_init_state_last_column(void)
{

    //printf ("ultima col %d\n",menu_ay_partitura_total_columns());
    menu_ay_partitura_ultima_columna[0]=menu_ay_partitura_total_columns()-1;
    menu_ay_partitura_ultima_columna[1]=menu_ay_partitura_total_columns()-1;
    menu_ay_partitura_ultima_columna[2]=menu_ay_partitura_total_columns()-1;
}

void menu_aysheet_change_chip(MENU_ITEM_PARAMETERS)
{
	menu_ay_partitura_chip++;
	if (menu_ay_partitura_chip==total_ay_chips) menu_ay_partitura_chip=0;
}

zxvision_window zxvision_window_ay_partitura;


void menu_ay_partitura(MENU_ITEM_PARAMETERS)
{



    menu_espera_no_tecla();

    if (!menu_multitarea) {
        menu_warn_message("This window needs multitask enabled");
        return;
    }

    zxvision_window *ventana;
    ventana=&zxvision_window_ay_partitura;

    //Inicializar array de estado
    menu_ay_partitura_init_state();

    //IMPORTANTE! no crear ventana si ya existe. Esto hay que hacerlo en todas las ventanas que permiten background.
    //si no se hiciera, se crearia la misma ventana, y en la lista de ventanas activas , al redibujarse,
    //la primera ventana repetida apuntaria a la segunda, que es el mismo puntero, y redibujaria la misma, y se quedaria en bucle colgado
	//zxvision_delete_window_if_exists(ventana);


    //Crear ventana si no existe
    if (!zxvision_if_window_already_exists(ventana)) {

        int xventana,yventana,ancho_ventana,alto_ventana,is_minimized,is_maximized,ancho_antes_minimize,alto_antes_minimize;


        char *titulo_ventana="Au. Chip Sheet (60 BPM)";


        if (!util_find_window_geometry("aysheet",&xventana,&yventana,&ancho_ventana,&alto_ventana,&is_minimized,&is_maximized,&ancho_antes_minimize,&alto_antes_minimize)) {

            //xventana=PIANO_PARTITURA_GRAPHIC_BASE_X;
            //yventana=PIANO_PARTITURA_GRAPHIC_BASE_Y;
            ancho_ventana=PIANO_PARTITURA_ANCHO_VENTANA;
            alto_ventana=PIANO_PARTITURA_ALTO_VENTANA;

            xventana=menu_center_x()-ancho_ventana/2;
            yventana=menu_center_y()-alto_ventana/2;

            int ancho_titulo=menu_da_ancho_titulo(titulo_ventana);

            //Para que se lea el titulo de la ventana en tamaño por defecto
            if (ancho_ventana<ancho_titulo) ancho_ventana=ancho_titulo;

        }




        //int ancho_titulo=menu_da_ancho_titulo(titulo_ventana);

        //Para que siempre se lea el titulo de la ventana
        //No alteramos el ancho, que sea el que tenga por geometria
        //if (ancho_ventana<ancho_titulo) ancho_ventana=ancho_titulo;

        //printf ("ancho %d\n",ancho_ventana);

        zxvision_new_window_gn_cim(ventana,xventana,yventana,ancho_ventana,alto_ventana,ancho_ventana-1,alto_ventana-2,titulo_ventana,
            "aysheet",is_minimized,is_maximized,ancho_antes_minimize,alto_antes_minimize);

        //zxvision_new_window_nocheck_staticsize(ventana,xventana,yventana,ancho_ventana,alto_ventana,ancho_ventana-1,alto_ventana-2,titulo_ventana);


        ventana->can_be_backgrounded=1;
        //indicar nombre del grabado de geometria
        //strcpy(ventana->geometry_name,"aysheet");
        //restaurar estado minimizado de ventana
        //ventana->is_minimized=is_minimized;

        //decimos que tiene que borrar fondo cada vez al redibujar
        //por tanto es como decirle que no use cache de putchar
        //dado que el fondo de texto es casi todo texto con caracter " " eso borra los pixeles que metemos con overlay del frame anterior
        ventana->must_clear_cache_on_draw=1;

    }

    //Si ya existe, activar esta ventana
    else {
        zxvision_activate_this_window(ventana);
    }

    zxvision_draw_window(ventana);



    //Comprobacion inicial de que el chip seleccionado no es mayor que los disponibles
    if (menu_ay_partitura_chip>=total_ay_chips) menu_ay_partitura_chip=0;


    //printf ("ancho creada %d\n",ventana->visible_width);

    menu_ay_partitura_overlay_window=ventana;

    menu_ay_partitura_init_state_last_column();


    //Cambiamos funcion overlay de texto de menu
    //Se establece a la de funcion de piano + texto

    //cambio overlay
    zxvision_set_window_overlay(ventana,menu_ay_partitura_overlay);

    //Toda ventana que este listada en zxvision_known_window_names_array debe permitir poder salir desde aqui
    //Se sale despues de haber inicializado overlay y de cualquier otra variable que necesite el overlay
    if (zxvision_currently_restoring_windows_on_start) {
        //printf ("Saliendo de ventana ya que la estamos restaurando en startup\n");
        return;
    }


    int retorno_menu=MENU_RETORNO_NORMAL;
    //Inicializado aqui a MENU_RETORNO_NORMAL en particular pues si solo hay 1 chip, no muestra selector de chip
    //y por tanto esta variable tiene que tener algo diferente de MENU_RETORNO_BACKGROUND


    //Si solo hay 1 chip, no mostrar selector de chip
    if (total_ay_chips==1) {
        int tecla=zxvision_wait_until_esc(ventana);
        if (tecla==3) {
            //Truco para decir que nos vamos a background
            retorno_menu=MENU_RETORNO_BACKGROUND;
        }
    }

    else {

        //Los array de menu_item no tienen porque cambiar el nombre en cada sitio que se usen
        menu_item *array_menu_nonamed;
        menu_item item_seleccionado;

        int nonamed_opcion_seleccionada=0; //Solo 1 item de menu, no tiene sentido guardar posicion


        do {


            menu_add_item_menu_inicial_format(&array_menu_nonamed,MENU_OPCION_NORMAL,menu_aysheet_change_chip,NULL,"[%d] Selected ~~Chip",menu_ay_partitura_chip+1);
            menu_add_item_menu_shortcut(array_menu_nonamed,'c');

            //Evito tooltips en los menus tabulados que tienen overlay porque al salir el tooltip detiene el overlay
            //menu_add_item_menu_tooltip(array_menu_nonamed,"Change wave Shape");
            menu_add_item_menu_ayuda(array_menu_nonamed,"Change selected chip");
            menu_add_item_menu_tabulado(array_menu_nonamed,1,0);



            //Nombre de ventana solo aparece en el caso de stdout
            retorno_menu=menu_dibuja_menu_no_title_lang(&nonamed_opcion_seleccionada,&item_seleccionado,array_menu_nonamed,"Audio Chip Sheet (60 BPM)" );


            if (retorno_menu!=MENU_RETORNO_BACKGROUND) {

                //En caso de menus tabulados, es responsabilidad de este de borrar la ventana

                if ((item_seleccionado.tipo_opcion&MENU_OPCION_ESC)==0 && retorno_menu>=0) {
                    //llamamos por valor de funcion
                    if (item_seleccionado.menu_funcion!=NULL) {
                        //printf ("actuamos por funcion\n");
                        item_seleccionado.menu_funcion(item_seleccionado.valor_opcion);

                    }
                }
            }

        } while ( (item_seleccionado.tipo_opcion&MENU_OPCION_ESC)==0 && retorno_menu!=MENU_RETORNO_ESC && !salir_todos_menus && retorno_menu!=MENU_RETORNO_BACKGROUND);
    }







    util_add_window_geometry_compact(ventana);


	if (retorno_menu==MENU_RETORNO_BACKGROUND) {
        zxvision_message_put_window_background();
	}

	else {
		zxvision_destroy_window(ventana);
	}


}







void menu_record_mid_start(MENU_ITEM_PARAMETERS)
{

	if (mid_has_been_initialized()) {
		if (!menu_confirm_yesno_texto("Will empty buffer","Sure?")) return;
	}

	mid_initialize_export();
	mid_is_recording.v=1;
}



void menu_record_mid_stop(MENU_ITEM_PARAMETERS)
{
	if (mid_has_been_initialized()) {
		if (!menu_confirm_yesno_texto("Stop recording","Sure?")) return;
	}
	mid_is_recording.v=0;
}


void menu_record_mid_pause_unpause(MENU_ITEM_PARAMETERS)
{
	mid_is_paused.v ^=1;
}

void menu_record_mid_save(MENU_ITEM_PARAMETERS)
{
        char file_save[PATH_MAX];

        char *filtros[2];

        filtros[0]="mid";
    filtros[1]=0;


    //guardamos directorio actual
    char directorio_actual[PATH_MAX];
    getcwd(directorio_actual,PATH_MAX);

	//Obtenemos ultimo directorio visitado
        if (mid_export_file[0]!=0) {
                char directorio[PATH_MAX];
                util_get_dir(mid_export_file,directorio);
                //printf ("strlen directorio: %d directorio: %s\n",strlen(directorio),directorio);

                //cambiamos a ese directorio, siempre que no sea nulo
                if (directorio[0]!=0) {
                        debug_printf (VERBOSE_INFO,"Changing to last directory: %s",directorio);
                        zvfs_chdir(directorio);
                }
        }

    int ret;

        ret=menu_filesel_save("Mid file",filtros,file_save);

        //volvemos a directorio inicial
        zvfs_chdir(directorio_actual);

        if (ret==1) {

                //Ver si archivo existe y preguntar
                if (si_existe_archivo(file_save)) {

                        if (menu_confirm_yesno_texto("File exists","Overwrite?")==0) return;

       			}

			strcpy(mid_export_file,file_save);
			mid_flush_file();

	        menu_generic_message_splash("Save MID","OK File saved");


        }
}

void menu_record_mid_noisetone(MENU_ITEM_PARAMETERS)
{
	mid_record_noisetone.v ^=1;
}


void menu_record_mid_instrument(MENU_ITEM_PARAMETERS)
{


    //Dado que es una variable local, siempre podemos usar este nombre array_menu_common
    menu_item *array_menu_common;
    menu_item item_seleccionado;
    int retorno_menu;


    do {

        menu_add_item_menu_inicial(&array_menu_common,"",MENU_OPCION_UNASSIGNED,NULL,NULL);

        int i;

        for (i=0;i<128;i++) {

            menu_add_item_menu_format(array_menu_common,MENU_OPCION_NORMAL,NULL,NULL,midi_instrument_list[i]);

        }



        menu_add_item_menu_separator(array_menu_common);

        menu_add_ESC_item(array_menu_common);

        retorno_menu=menu_dibuja_menu_dialogo_no_title_lang(&record_mid_instrument_opcion_seleccionada,&item_seleccionado,array_menu_common,"Instrument");

        //no queremos que al pulsar ESC aqui se cierren todos los menus anteriores
        salir_todos_menus=0;

        if ((item_seleccionado.tipo_opcion&MENU_OPCION_ESC)==0 && retorno_menu>=0) {
            //Cambiar instrumento y salir
            mid_instrument=record_mid_instrument_opcion_seleccionada;
            mid_set_cambio_instrumento();
            menu_generic_message_splash("Change instrument","OK. Instrument changed");
            salir_todos_menus=1;
            return;
        }

    } while ( (item_seleccionado.tipo_opcion&MENU_OPCION_ESC)==0 && retorno_menu!=MENU_RETORNO_ESC && !salir_todos_menus);


}

int menu_cond_ay_or_sn_chip(void)
{
	if (ay_chip_present.v || sn_chip_present.v || i8049_chip_present ) return 1;
	else return 0;
}

void menu_record_mid(MENU_ITEM_PARAMETERS)
{
        menu_item *array_menu_record_mid;
	menu_item item_seleccionado;
	int retorno_menu;

        do {

                    menu_add_item_menu_inicial(&array_menu_record_mid,"",MENU_OPCION_UNASSIGNED,NULL,NULL);

					if (mid_is_recording.v==0) {
						menu_add_item_menu_en_es_ca(array_menu_record_mid,MENU_OPCION_NORMAL,menu_record_mid_start,menu_cond_ay_or_sn_chip,
                            "Start Recording","Iniciar Grabacion","Iniciar Gravacio");
					}

					else {
						menu_add_item_menu_en_es_ca(array_menu_record_mid,MENU_OPCION_NORMAL,menu_record_mid_stop,menu_cond_ay_or_sn_chip,
                            "Stop Recording","Detener Grabacion","Aturar Gravacio");
					}






					if (mid_is_recording.v) {

						if (mid_is_paused.v==0) {
							menu_add_item_menu_en_es_ca(array_menu_record_mid,MENU_OPCION_NORMAL,menu_record_mid_pause_unpause,menu_cond_ay_or_sn_chip,
                                "Pause Recording","Pausar Grabacion","Pausar Gravacio");
						}

						else {
							menu_add_item_menu_en_es_ca(array_menu_record_mid,MENU_OPCION_NORMAL,menu_record_mid_pause_unpause,menu_cond_ay_or_sn_chip,
                                "Resume Recording","Continuar Grabacion","Continuar Gravacio");
						}



					}

					//No dejamos grabar hasta que no se haga stop
					//porque el flush del final mete cabeceras de final de pistas y ya no se puede reaprovechar
					if (mid_has_been_initialized() && mid_notes_recorded && mid_is_recording.v==0) {
						menu_add_item_menu_en_es_ca(array_menu_record_mid,MENU_OPCION_NORMAL,menu_record_mid_save,menu_cond_ay_or_sn_chip,
                            "Save .MID file","Grabar archivo .MID","Gravar arxiu .MID");
					}


					menu_add_item_menu_format(array_menu_record_mid,MENU_OPCION_SEPARADOR,NULL,NULL,"");
					menu_add_item_menu_format(array_menu_record_mid,MENU_OPCION_NORMAL,menu_record_mid_noisetone,NULL,"[%c] Allow tone+noise",
						(mid_record_noisetone.v ? 'X' : ' ') );
					menu_add_item_menu_tooltip(array_menu_record_mid,"Record also channels enabled as tone+noise");
					menu_add_item_menu_ayuda(array_menu_record_mid,"Record also channels enabled as tone+noise");


                    menu_add_item_menu_en_es_ca(array_menu_record_mid,MENU_OPCION_NORMAL,menu_record_mid_instrument,NULL,
                        "Change Instrument","Cambiar Instrumento","Canviar Instrument");
                    menu_add_item_menu_genera_ventana(array_menu_record_mid);

					if (mid_notes_recorded) {

						int max_buffer=mid_max_buffer();


						int max_buffer_perc=(max_buffer*100)/MAX_MID_EXPORT_BUFFER;

						//printf ("%d %d\n",max_buffer,max_buffer_perc);


						menu_add_item_menu_format(array_menu_record_mid,MENU_OPCION_SEPARADOR,NULL,NULL,"");
						menu_add_item_menu_format(array_menu_record_mid,MENU_OPCION_SEPARADOR,NULL,NULL,"Info:");
						menu_add_item_menu_format(array_menu_record_mid,MENU_OPCION_SEPARADOR,NULL,NULL,"Buffer used: %d%%",max_buffer_perc);
						menu_add_item_menu_format(array_menu_record_mid,MENU_OPCION_SEPARADOR,NULL,NULL,"Voices: %d",3*mid_chips_al_start);
						menu_add_item_menu_format(array_menu_record_mid,MENU_OPCION_SEPARADOR,NULL,NULL,"Notes recorded: %d",mid_notes_recorded);


					}





                menu_add_item_menu(array_menu_record_mid,"",MENU_OPCION_SEPARADOR,NULL,NULL);


                //menu_add_item_menu(array_menu_record_mid,"ESC Back",MENU_OPCION_NORMAL|MENU_OPCION_ESC,NULL,NULL);
		menu_add_ESC_item(array_menu_record_mid);

                retorno_menu=menu_dibuja_menu_no_title_lang(&record_mid_opcion_seleccionada,&item_seleccionado,array_menu_record_mid,"Audio Chip to .mid" );



		if ((item_seleccionado.tipo_opcion&MENU_OPCION_ESC)==0 && retorno_menu>=0) {
	                //llamamos por valor de funcion
        	        if (item_seleccionado.menu_funcion!=NULL) {
                	        //printf ("actuamos por funcion\n");
	                        item_seleccionado.menu_funcion(item_seleccionado.valor_opcion);

        	        }
		}

	} while ( (item_seleccionado.tipo_opcion&MENU_OPCION_ESC)==0 && retorno_menu!=MENU_RETORNO_ESC && !salir_todos_menus);
}



//Funcion comun de midi output de alsa y coreaudio
void menu_midi_output_noisetone(MENU_ITEM_PARAMETERS)
{
	midi_output_record_noisetone.v ^=1;
}

//Funcion comun de midi output de alsa y coreaudio
void menu_midi_output_initialize(MENU_ITEM_PARAMETERS)
{



	if (audio_midi_output_initialized==0) {
		if (audio_midi_output_init() ) {
			menu_error_message("Error initializing midi device");
		}
	}
	else {
		audio_midi_output_finish();
		audio_midi_output_initialized=0;
	}
}

int menu_midi_output_initialized_cond(void)
{
	return !audio_midi_output_initialized;
}


void menu_midi_output_client(MENU_ITEM_PARAMETERS)
{
        char string_valor[4];
        int valor;


        sprintf (string_valor,"%d",audio_midi_client);


        menu_ventana_scanf("Client value",string_valor,4);

        valor=parse_string_to_number(string_valor);
	if (valor<0 || valor>255) {
		menu_error_message("Invalid client value");
	}


	audio_midi_client=valor;

}

void menu_midi_output_port(MENU_ITEM_PARAMETERS)
{
        char string_valor[4];
        int valor;


        sprintf (string_valor,"%d",audio_midi_port);


        menu_ventana_scanf("Port value",string_valor,4);

        valor=parse_string_to_number(string_valor);
        if (valor<0 || valor>255) {
                menu_error_message("Invalid client value");
        }


        audio_midi_port=valor;

}



//Listar dispositivos midi. Solo tiene sentido esto en Linux
void menu_direct_alsa_midi_output_list_devices(MENU_ITEM_PARAMETERS)
{

	char *device_list="/proc/asound/seq/clients";

	if (!si_existe_archivo(device_list)) {
		menu_error_message("Can not find device list");
		return;
	}

	//Abrir archivo y mostrarlo en ventana
	//Usamos esta funcion generica de mostrar archivos de ayuda
	menu_about_read_file("Sequencer devices",device_list,1);

}


void menu_direct_midi_output_rawmode(MENU_ITEM_PARAMETERS)
{
	audio_midi_raw_mode ^=1;
}

#ifdef COMPILE_ALSA
void menu_direct_alsa_midi_device_raw(MENU_ITEM_PARAMETERS)
{
    menu_ventana_scanf("MIDI device name",audio_raw_midi_device_out,MAX_AUDIO_RAW_MIDI_DEVICE_OUT);

}
#endif


void menu_midi_output_test(MENU_ITEM_PARAMETERS)
{
	//audio_midi_output_note_on(0, 60); //60=central DO

	// Does it sound familiar? ;)
	audio_midi_output_note_on(0, 61); //61=C#

	audio_midi_output_note_on(0, 58); //58=A#

	audio_midi_output_note_on(0, 54); //54=F#
}


void menu_midi_output_reset(MENU_ITEM_PARAMETERS)
{
	audio_midi_output_reset();

}


void menu_midi_output_instrument(MENU_ITEM_PARAMETERS)
{


    //Dado que es una variable local, siempre podemos usar este nombre array_menu_common
    menu_item *array_menu_common;
    menu_item item_seleccionado;
    int retorno_menu;


    do {

        menu_add_item_menu_inicial(&array_menu_common,"",MENU_OPCION_UNASSIGNED,NULL,NULL);

        int i;

        for (i=0;i<128;i++) {

            menu_add_item_menu_format(array_menu_common,MENU_OPCION_NORMAL,NULL,NULL,midi_instrument_list[i]);

        }



        menu_add_item_menu_separator(array_menu_common);

        menu_add_ESC_item(array_menu_common);

        retorno_menu=menu_dibuja_menu_dialogo_no_title_lang(&midi_output_instrument_opcion_seleccionada,&item_seleccionado,array_menu_common,"Instrument");

        //no queremos que al pulsar ESC aqui se cierren todos los menus anteriores
        salir_todos_menus=0;

        if ((item_seleccionado.tipo_opcion&MENU_OPCION_ESC)==0 && retorno_menu>=0) {
            //Cambiar instrumento y salir

            audio_midi_set_instrument(midi_output_instrument_opcion_seleccionada);
            menu_generic_message_splash("Change instrument","OK. Instrument changed");
            salir_todos_menus=1;
            return;
        }

    } while ( (item_seleccionado.tipo_opcion&MENU_OPCION_ESC)==0 && retorno_menu!=MENU_RETORNO_ESC && !salir_todos_menus);


}


void menu_direct_midi_output(MENU_ITEM_PARAMETERS)
{
        menu_item *array_menu_direct_midi_output;
	menu_item item_seleccionado;
	int retorno_menu;

        do {


		menu_add_item_menu_inicial(&array_menu_direct_midi_output,"",MENU_OPCION_UNASSIGNED,NULL,NULL);



#ifdef COMPILE_ALSA
		//En Alsa Linux
		menu_add_item_menu_format(array_menu_direct_midi_output,MENU_OPCION_NORMAL,menu_direct_midi_output_rawmode,menu_midi_output_initialized_cond,"[%c] MIDI Raw mode",(audio_midi_raw_mode ? 'X' : ' ' ));
		menu_add_item_menu_tooltip(array_menu_direct_midi_output,"RAW mode is needed to emulate AY MIDI registers");
		menu_add_item_menu_ayuda(array_menu_direct_midi_output,"RAW mode is needed to emulate AY MIDI registers");

		if (audio_midi_raw_mode) {


                char string_device_shown[10];
                menu_tape_settings_trunc_name(audio_raw_midi_device_out,string_device_shown,10);

			menu_add_item_menu_format(array_menu_direct_midi_output,MENU_OPCION_NORMAL,menu_direct_alsa_midi_device_raw,menu_midi_output_initialized_cond,"Device: %s",string_device_shown);
		}

		else {
			menu_add_item_menu_format(array_menu_direct_midi_output,MENU_OPCION_NORMAL,menu_direct_alsa_midi_output_list_devices,NULL,"List midi devices");
			menu_add_item_menu_format(array_menu_direct_midi_output,MENU_OPCION_NORMAL,menu_midi_output_client,menu_midi_output_initialized_cond,"[%d] Midi client",audio_midi_client);
			menu_add_item_menu_format(array_menu_direct_midi_output,MENU_OPCION_NORMAL,menu_midi_output_port,menu_midi_output_initialized_cond,"[%d] Midi port",audio_midi_port);
		}
#endif

#ifdef MINGW
		//en Windows
		menu_add_item_menu_format(array_menu_direct_midi_output,MENU_OPCION_NORMAL,menu_midi_output_port,menu_midi_output_initialized_cond,"[%d] Midi port",audio_midi_port);
#endif



		/*if (audio_midi_output_initialized==0) {
			menu_add_item_menu_format(array_menu_direct_midi_output,MENU_OPCION_NORMAL,menu_midi_output_initialize,NULL,"Initialize midi");
		}
		else {
			menu_add_item_menu_format(array_menu_direct_midi_output,MENU_OPCION_NORMAL,menu_midi_output_initialize,NULL,"Disable midi");
		}*/

		menu_add_item_menu_format(array_menu_direct_midi_output,MENU_OPCION_NORMAL,menu_midi_output_initialize,NULL,"[%c] Initialized",
			(audio_midi_output_initialized ? 'X' : ' ' ) );


		if (audio_midi_output_initialized) {
			menu_add_item_menu_format(array_menu_direct_midi_output,MENU_OPCION_NORMAL,menu_midi_output_test,NULL,"Test MIDI");

            menu_add_item_menu_format(array_menu_direct_midi_output,MENU_OPCION_NORMAL,menu_midi_output_instrument,NULL,"Change instrument");
            menu_add_item_menu_genera_ventana(array_menu_direct_midi_output);


			menu_add_item_menu_format(array_menu_direct_midi_output,MENU_OPCION_NORMAL,menu_midi_output_reset,NULL,"Reset channels");
		}

		//Parece que no funciona la gestion de volumen
		//menu_add_item_menu_format(array_menu_direct_alsa_midi_output,MENU_OPCION_NORMAL,menu_direct_alsa_midi_output_volume,NULL,"Volume: %d%%",alsa_midi_volume);



					menu_add_item_menu_format(array_menu_direct_midi_output,MENU_OPCION_SEPARADOR,NULL,NULL,"");
					menu_add_item_menu_format(array_menu_direct_midi_output,MENU_OPCION_NORMAL,menu_midi_output_noisetone,NULL,"[%c] Allow tone+noise",
						(midi_output_record_noisetone.v ? 'X' : ' ') );
					menu_add_item_menu_tooltip(array_menu_direct_midi_output,"Send also channels enabled as tone+noise");
					menu_add_item_menu_ayuda(array_menu_direct_midi_output,"Send also channels enabled as tone+noise");


                menu_add_item_menu(array_menu_direct_midi_output,"",MENU_OPCION_SEPARADOR,NULL,NULL);


                //menu_add_item_menu(array_menu_direct_midi_output,"ESC Back",MENU_OPCION_NORMAL|MENU_OPCION_ESC,NULL,NULL);
		menu_add_ESC_item(array_menu_direct_midi_output);

                retorno_menu=menu_dibuja_menu_no_title_lang(&direct_midi_output_opcion_seleccionada,&item_seleccionado,array_menu_direct_midi_output,"Audio Chip to MIDI output" );



		if ((item_seleccionado.tipo_opcion&MENU_OPCION_ESC)==0 && retorno_menu>=0) {
	                //llamamos por valor de funcion
        	        if (item_seleccionado.menu_funcion!=NULL) {
                	        //printf ("actuamos por funcion\n");
	                        item_seleccionado.menu_funcion(item_seleccionado.valor_opcion);

        	        }
		}

	} while ( (item_seleccionado.tipo_opcion&MENU_OPCION_ESC)==0 && retorno_menu!=MENU_RETORNO_ESC && !salir_todos_menus);
}







//cambia filtro
void menu_ay_mixer_cambia_filtro(MENU_ITEM_PARAMETERS)
{

	int chip=valor_opcion/3;
	int canal=valor_opcion % 3;

	//printf ("chip %d canal %d\n",chip,canal);

	z80_byte valor_filtro=ay_filtros[chip];
	z80_byte mascara=1|8; //bits xxxx1xx1

	z80_byte mascara_ceros=1|8;

	if (canal>0) {
		mascara=mascara<<canal;
		mascara_ceros=mascara_ceros<<canal;
	}

	//aplicar mascara
	valor_filtro &=mascara;

	//Normalizar
	if (canal>0) valor_filtro=valor_filtro>>canal;


	//Valores posibles 0,1,8,9
	if (valor_filtro==0) valor_filtro=1;
	else if (valor_filtro==1) valor_filtro=8;
	else if (valor_filtro==8) valor_filtro=9;
	else valor_filtro=0;

	//Volver a meter donde estaba
	if (canal>0) {
		valor_filtro=valor_filtro<<canal;
	}

	mascara_ceros=255-mascara_ceros;

	//Poner a ceros los que habia
	ay_filtros[chip] &=mascara_ceros;

	//Poner filtro actual
	ay_filtros[chip] |=valor_filtro;

}

//Muestra cadena filtro
void menu_ay_mixer_retorna_filtro(int chip,int canal,char *destino)
{
	z80_byte valor_filtro=ay_filtros[chip];
	z80_byte mascara=1|8; //bits xxxx1xx1

	if (canal>0) mascara=mascara<<canal;

	//aplicar mascara
	valor_filtro &=mascara;

	//Normalizar
	if (canal>0) valor_filtro=valor_filtro>>canal;

	//Posibles valores: 0,1,8,9
	switch (valor_filtro) {
		case 0:
			strcpy(destino,"No filter");
		break;

		case 1:
			strcpy(destino,"No tone  ");
		break;

		case 8:
			strcpy(destino,"No noise ");
		break;

		//case 9:
		default:
			strcpy(destino,"Silence  ");
		break;

	}

	return;
}


void menu_audio_envelopes(MENU_ITEM_PARAMETERS)
{
	ay_envelopes_enabled.v^=1;
}

void menu_audio_speech(MENU_ITEM_PARAMETERS)
{
        ay_speech_enabled.v^=1;
}

void menu_audio_aymid_rs232(MENU_ITEM_PARAMETERS)
{
        aymidi_rs232_enabled.v^=1;
}

void menu_audio_ay_stereo_custom_A(MENU_ITEM_PARAMETERS)
{
	ay3_custom_stereo_A++;
	if (ay3_custom_stereo_A==3) ay3_custom_stereo_A=0;
}

void menu_audio_ay_stereo_custom_B(MENU_ITEM_PARAMETERS)
{
	ay3_custom_stereo_B++;
	if (ay3_custom_stereo_B==3) ay3_custom_stereo_B=0;
}

void menu_audio_ay_stereo_custom_C(MENU_ITEM_PARAMETERS)
{
	ay3_custom_stereo_C++;
	if (ay3_custom_stereo_C==3) ay3_custom_stereo_C=0;
}

void menu_audio_ay_stereo(MENU_ITEM_PARAMETERS)
{
	ay3_stereo_mode++;

	if (ay3_stereo_mode==6) ay3_stereo_mode=0;
}


char *menu_stereo_positions[]={
	"Left",
	"    Center",
	"          Right"
};

void menu_ay_mixer(MENU_ITEM_PARAMETERS)
{
menu_item *array_menu_ay_mixer;
	menu_item item_seleccionado;
	int retorno_menu;

	char buffer_filtro[33];


        do {


		menu_add_item_menu_inicial_format(&array_menu_ay_mixer,MENU_OPCION_NORMAL,menu_audio_envelopes,menu_cond_ay_chip,"[%c] AY ~~Envelopes", (ay_envelopes_enabled.v==1 ? 'X' : ' '));
		menu_add_item_menu_shortcut(array_menu_ay_mixer,'e');
		menu_add_item_menu_tooltip(array_menu_ay_mixer,"Enable or disable volume envelopes for the AY Chip");
		menu_add_item_menu_ayuda(array_menu_ay_mixer,"Enable or disable volume envelopes for the AY Chip");

		menu_add_item_menu_format(array_menu_ay_mixer,MENU_OPCION_NORMAL,menu_audio_speech,menu_cond_ay_chip,"[%c] AY ~~Speech", (ay_speech_enabled.v==1 ? 'X' : ' '));
		menu_add_item_menu_shortcut(array_menu_ay_mixer,'s');
		menu_add_item_menu_tooltip(array_menu_ay_mixer,"Enable or disable AY Speech effects");
		menu_add_item_menu_ayuda(array_menu_ay_mixer,"These effects are used, for example, in Chase H.Q.");


		menu_add_item_menu_format(array_menu_ay_mixer,MENU_OPCION_NORMAL,menu_audio_aymid_rs232,menu_cond_ay_chip,"[%c] AY ~~MIDI registers", (aymidi_rs232_enabled.v==1 ? 'X' : ' '));
		menu_add_item_menu_shortcut(array_menu_ay_mixer,'m');
		menu_add_item_menu_tooltip(array_menu_ay_mixer,"Enable or disable AY MIDI registers");
		menu_add_item_menu_ayuda(array_menu_ay_mixer,"It handles midi commands sent to register 14 and 15 and are routed to a "
            "external midi device (enable it on Settings-> Audio-> Audio Chip to MIDI Output");


		if (MACHINE_IS_SPECTRUM || MACHINE_IS_MSX || MACHINE_IS_SVI || MACHINE_IS_CPC) {


			char ay3_stereo_string[16];
			if (ay3_stereo_mode==1) strcpy(ay3_stereo_string,"ACB");
			else if (ay3_stereo_mode==2) strcpy(ay3_stereo_string,"ABC");
			else if (ay3_stereo_mode==3) strcpy(ay3_stereo_string,"BAC");
			else if (ay3_stereo_mode==4) strcpy(ay3_stereo_string,"CBA");
            else if (ay3_stereo_mode==5) strcpy(ay3_stereo_string,"Custom");
			else strcpy(ay3_stereo_string,"Mono");
            /*
            CBA Es para Amstrad CPC:
            Mono and Stereo Output
            When using the CPC's external stereo jack,
            channel A is output to the right, channel C is output left,
            and channel B is output to both left and right,
            in that case channel B is output through a bigger resistor to prevent that this channel appears loader than the others.

            Otherwise (when using the built-in speaker), all three channels are mixed at the same intensity.
            This signal appears to be also sent to the Tape output line also, so a connected Data Recorder could be used to record CPC music also.

            */

			menu_add_item_menu_format(array_menu_ay_mixer,MENU_OPCION_NORMAL,menu_audio_ay_stereo,menu_cond_ay_chip,"    AY S~~tereo: %s",
				ay3_stereo_string);
			menu_add_item_menu_shortcut(array_menu_ay_mixer,'t');

			if (ay3_stereo_mode==5) {

				menu_add_item_menu_format(array_menu_ay_mixer,MENU_OPCION_NORMAL,menu_audio_ay_stereo_custom_A,menu_cond_ay_chip,
					"    Ch. A: %s",menu_stereo_positions[ay3_custom_stereo_A]);

				menu_add_item_menu_format(array_menu_ay_mixer,MENU_OPCION_NORMAL,menu_audio_ay_stereo_custom_B,menu_cond_ay_chip,
					"    Ch. B: %s",menu_stereo_positions[ay3_custom_stereo_B]);

				menu_add_item_menu_format(array_menu_ay_mixer,MENU_OPCION_NORMAL,menu_audio_ay_stereo_custom_C,menu_cond_ay_chip,
					"    Ch. C: %s",menu_stereo_positions[ay3_custom_stereo_C]);


			}

		}



			int chip;

			for (chip=0;chip<audio_get_total_chips();chip++) {
				int canal;
				menu_add_item_menu_format(array_menu_ay_mixer,MENU_OPCION_SEPARADOR,NULL,NULL,"---Chip %d---",chip+1);

				for (canal=0;canal<3;canal++) {
					menu_ay_mixer_retorna_filtro(chip,canal,buffer_filtro);
					menu_add_item_menu_format(array_menu_ay_mixer,MENU_OPCION_NORMAL,menu_ay_mixer_cambia_filtro,NULL,"[%s] Channel %c",buffer_filtro,'A'+canal);

					menu_add_item_menu_valor_opcion(array_menu_ay_mixer,chip*3+canal);

				}
			}




                menu_add_item_menu(array_menu_ay_mixer,"",MENU_OPCION_SEPARADOR,NULL,NULL);


                //menu_add_item_menu(array_menu_ay_mixer,"ESC Back",MENU_OPCION_NORMAL|MENU_OPCION_ESC,NULL,NULL);
		menu_add_ESC_item(array_menu_ay_mixer);

                retorno_menu=menu_dibuja_menu_no_title_lang(&ay_mixer_opcion_seleccionada,&item_seleccionado,array_menu_ay_mixer,"AY mixer" );



		if ((item_seleccionado.tipo_opcion&MENU_OPCION_ESC)==0 && retorno_menu>=0) {
	                //llamamos por valor de funcion
        	        if (item_seleccionado.menu_funcion!=NULL) {
                	        //printf ("actuamos por funcion\n");
	                        item_seleccionado.menu_funcion(item_seleccionado.valor_opcion);

        	        }
		}

	} while ( (item_seleccionado.tipo_opcion&MENU_OPCION_ESC)==0 && retorno_menu!=MENU_RETORNO_ESC && !salir_todos_menus);
}


void menu_i8049_mixer_pitch2(MENU_ITEM_PARAMETERS)
{
    ql_sound_feature_pitch2_enabled ^=1;
}

void menu_i8049_mixer_grad_x(MENU_ITEM_PARAMETERS)
{
    ql_sound_feature_grad_x_enabled ^=1;
}

void menu_i8049_mixer_grad_y(MENU_ITEM_PARAMETERS)
{
    ql_sound_feature_grad_y_enabled ^=1;
}

void menu_i8049_mixer_wrap(MENU_ITEM_PARAMETERS)
{
    ql_sound_feature_wrap_enabled ^=1;
}


void menu_i8049_mixer_fuzzy(MENU_ITEM_PARAMETERS)
{
    ql_sound_feature_fuzzy_enabled ^=1;
}


void menu_i8049_mixer_random(MENU_ITEM_PARAMETERS)
{
    ql_sound_feature_random_enabled ^=1;
}

void menu_i8049_mixer_stop_sound(MENU_ITEM_PARAMETERS)
{
    ql_stop_sound();
}

void menu_i8049_mixer(MENU_ITEM_PARAMETERS)
{
    menu_item *array_menu_common;
    menu_item item_seleccionado;
    int retorno_menu;




    do {
/*
    if (!ql_sound_feature_pitch2_enabled) ql_audio_pitch2=0;
    if (!ql_sound_feature_grad_x_enabled) ql_audio_grad_x=0;
    if (!ql_sound_feature_grad_y_enabled) ql_audio_grad_y=0;

    if (!ql_sound_feature_wrap_enabled) ql_audio_wrap=0;
    if (!ql_sound_feature_fuzzy_enabled) ql_audio_fuziness=0;
    if (!ql_sound_feature_random_enabled) ql_audio_randomness_of_step=0;
    */

        menu_add_item_menu_inicial_format(&array_menu_common,MENU_OPCION_NORMAL,menu_i8049_mixer_pitch2,NULL,"[%c] Pitch 2", (ql_sound_feature_pitch2_enabled ? 'X' : ' '));

        menu_add_item_menu_format(array_menu_common,MENU_OPCION_NORMAL,menu_i8049_mixer_grad_x,NULL,"[%c] Grad_x", (ql_sound_feature_grad_x_enabled ? 'X' : ' '));

        menu_add_item_menu_format(array_menu_common,MENU_OPCION_NORMAL,menu_i8049_mixer_grad_y,NULL,"[%c] Grad_y", (ql_sound_feature_grad_y_enabled ? 'X' : ' '));

        menu_add_item_menu_format(array_menu_common,MENU_OPCION_NORMAL,menu_i8049_mixer_wrap,NULL,"[%c] Wrap", (ql_sound_feature_wrap_enabled ? 'X' : ' '));

        menu_add_item_menu_format(array_menu_common,MENU_OPCION_NORMAL,menu_i8049_mixer_fuzzy,NULL,"[%c] Fuzzy", (ql_sound_feature_fuzzy_enabled ? 'X' : ' '));

        menu_add_item_menu_format(array_menu_common,MENU_OPCION_NORMAL,menu_i8049_mixer_random,NULL,"[%c] Random", (ql_sound_feature_random_enabled ? 'X' : ' '));

        menu_add_item_menu_separator(array_menu_common);

        menu_add_item_menu_format(array_menu_common,MENU_OPCION_NORMAL,menu_i8049_mixer_stop_sound,NULL,"    Stop sound");


        menu_add_item_menu_separator(array_menu_common);



        menu_add_ESC_item(array_menu_common);

        retorno_menu=menu_dibuja_menu_no_title_lang(&i8049_mixer_opcion_seleccionada,&item_seleccionado,array_menu_common,"i8049 mixer" );



        if ((item_seleccionado.tipo_opcion&MENU_OPCION_ESC)==0 && retorno_menu>=0) {
            //llamamos por valor de funcion
            if (item_seleccionado.menu_funcion!=NULL) {
                //printf ("actuamos por funcion\n");
                item_seleccionado.menu_funcion(item_seleccionado.valor_opcion);

            }
        }

    } while ( (item_seleccionado.tipo_opcion&MENU_OPCION_ESC)==0 && retorno_menu!=MENU_RETORNO_ESC && !salir_todos_menus);
}


void menu_audio_chip_info(MENU_ITEM_PARAMETERS)
{

	int chip_frequency;
	int max_freq,min_freq;

	if (sn_chip_present.v) {
		//Chip SN
		chip_frequency=sn_chip_frequency;
		max_freq=sn_retorna_frecuencia_valor_registro(0,0);
		min_freq=sn_retorna_frecuencia_valor_registro(255,255);
	}

	else if (i8049_chip_present) {
		//Chip 8049 del QL
		chip_frequency=ql_i8049_sound_chip_frequency;
		max_freq=ql_ipc_get_frecuency_sound_value(1);
		min_freq=ql_ipc_get_frecuency_sound_value(255);
	}

	else {
		//Chip AY
		chip_frequency=ay_chip_frequency;
		max_freq=ay_retorna_frecuencia_valor_registro(0,0);
		min_freq=ay_retorna_frecuencia_valor_registro(255,255);
	}


	if (sn_chip_present.v) {
		//SN
		menu_generic_message_format("Audio Chip Info","Audio Chip: Texas Instruments SN76489AN\nFrequency: %d Hz\n"
									"Min Tone Frequency: %d Hz\nMax Tone Frequency: %d Hz\n"
									"3 Tone Channels, 1 Noise Channel",
			chip_frequency,min_freq,max_freq
		);
	}

	else if (i8049_chip_present) {
		//8049 del QL
		menu_generic_message_format("Audio Chip Info","Audio Chip: Intel 8049\nFrequency: %d MHz\n"
									"Min Tone Frequency: %d Hz\nMax Tone Frequency: %d Hz\n"
									"2 PseudoTone Channels, Noise Effects",
			chip_frequency/1000000,min_freq,max_freq
		);
	}

	else {
		//AY
		menu_generic_message_format("Audio Chip Info","Audio Chip: General Instrument AY-3-8910\nFrequency: %d Hz\n"
									"Min Tone Frequency: %d Hz\nMax Tone Frequency: %d Hz\n"
									"3 Noise/Tone Channels, 1 Envelope Generator",
			chip_frequency,min_freq,max_freq
		);
	}


}


void menu_uartbridge_file(MENU_ITEM_PARAMETERS)
{
	uartbridge_disable();

        char *filtros[2];

        filtros[0]="";
        filtros[1]=0;


        if (menu_filesel("Select Device File",filtros,uartbridge_name)==1) {
                if (!si_existe_archivo(uartbridge_name)) {
                        menu_error_message("File does not exist");
                        uartbridge_name[0]=0;
                        return;
                }


        }
        //Sale con ESC
        else {
                //Quitar nombre
                uartbridge_name[0]=0;


        }

}


void menu_uartbridge_enable(MENU_ITEM_PARAMETERS)
{
	if (uartbridge_enabled.v) uartbridge_disable();
	else uartbridge_enable();
}


int menu_uartbridge_cond(void)
{
	if (uartbridge_name[0]==0) return 0;

	else return 1;

}


int menu_uartbridge_speed_cond(void)
{
	if (uartbridge_enabled.v) return 0;

	else return 1;

}

void menu_uartbridge_speed(MENU_ITEM_PARAMETERS)
{
	if (uartbridge_speed==CHDEV_SPEED_115200) uartbridge_speed=CHDEV_SPEED_DEFAULT;
	else uartbridge_speed++;
}



void menu_uartbridge(MENU_ITEM_PARAMETERS)
{
        menu_item *array_menu_uartbridge;
        menu_item item_seleccionado;
        int retorno_menu;
        do {

                char string_uartbridge_file_shown[13];


                        menu_tape_settings_trunc_name(uartbridge_name,string_uartbridge_file_shown,13);
                        menu_add_item_menu_inicial_format(&array_menu_uartbridge,MENU_OPCION_NORMAL,menu_uartbridge_file,NULL,"~~File [%s]",string_uartbridge_file_shown);
                        menu_add_item_menu_shortcut(array_menu_uartbridge,'f');
                        menu_add_item_menu_tooltip(array_menu_uartbridge,"Path to the serial device");
                        menu_add_item_menu_ayuda(array_menu_uartbridge,"Path to the serial device");


						//Lo separamos en dos pues cuando no esta habilitado, tiene que comprobar que el path no sea nulo
						if (uartbridge_enabled.v) {
							menu_add_item_menu_format(array_menu_uartbridge,MENU_OPCION_NORMAL,menu_uartbridge_enable,NULL,"[X] ~~Enabled");
						}
						else {
							menu_add_item_menu_format(array_menu_uartbridge,MENU_OPCION_NORMAL,menu_uartbridge_enable,menu_uartbridge_cond,"[ ] ~~Enabled");
						}
						menu_add_item_menu_shortcut(array_menu_uartbridge,'e');


#ifndef MINGW

						if (uartbridge_speed==CHDEV_SPEED_DEFAULT) {
							menu_add_item_menu_format(array_menu_uartbridge,MENU_OPCION_NORMAL,menu_uartbridge_speed,menu_uartbridge_speed_cond,"[Default] Speed");
						}
						else {
							menu_add_item_menu_format(array_menu_uartbridge,MENU_OPCION_NORMAL,menu_uartbridge_speed,menu_uartbridge_speed_cond,"[%d] Speed",
							chardevice_getspeed_enum_int(uartbridge_speed));
						}

#endif


                        menu_add_item_menu(array_menu_uartbridge,"",MENU_OPCION_SEPARADOR,NULL,NULL);

                menu_add_ESC_item(array_menu_uartbridge);

                retorno_menu=menu_dibuja_menu_no_title_lang(&uartbridge_opcion_seleccionada,&item_seleccionado,array_menu_uartbridge,"UART Bridge" );


                if ((item_seleccionado.tipo_opcion&MENU_OPCION_ESC)==0 && retorno_menu>=0) {
                        //llamamos por valor de funcion
                        if (item_seleccionado.menu_funcion!=NULL) {
                                //printf ("actuamos por funcion\n");
                                item_seleccionado.menu_funcion(item_seleccionado.valor_opcion);

                        }
                }

        } while ( (item_seleccionado.tipo_opcion&MENU_OPCION_ESC)==0 && retorno_menu!=MENU_RETORNO_ESC && !salir_todos_menus);




}



int menu_network_uartbridge_cond(void)
{
	if (MACHINE_IS_ZXUNO || MACHINE_IS_TBBLUE || MACHINE_IS_TSCONF) return 1;
	else return 0;
}



int contador_menu_zeng_connect_print=0;



void menu_common_connect_print(zxvision_window *w,char *texto)
{
	char *mensaje="|/-\\";

	int max=strlen(mensaje);
    //Suficiente para que quepa el texto
	char mensaje_dest[NETWORK_MAX_URL+256];

	int pos=contador_menu_zeng_connect_print % max;

	sprintf(mensaje_dest,"%s %c",texto,mensaje[pos]);
	//printf ("pos: %d\n",pos);

	//zxvision_print_string_defaults_fillspc(w,1,0,mensaje_dest);

    //Escribir el texto troceado
    //en el caso de haber llamado aqui desde menu_download_file_connect_print,
    //igual no tiene mucho sentido trocear pues ya estamos cortando el texto para que quepa en una sola linea
    zxvision_print_mensaje_lineas_troceado(w,mensaje_dest);

	zxvision_draw_window_contents(w);

	contador_menu_zeng_connect_print++;

}

//Para indicar desde que host se hace conexion
char menu_zeng_connect_print_host[NETWORK_MAX_URL]="";
void menu_zeng_connect_print(zxvision_window *w)
{
    char buf_temp[NETWORK_MAX_URL+256];
    sprintf(buf_temp,"Connecting to %s",menu_zeng_connect_print_host);
	//menu_common_connect_print(w,"Connecting");
    menu_common_connect_print(w,buf_temp);
}

//Para indicar desde que host se hace descarga
char menu_download_file_connect_print_host[NETWORK_MAX_URL]="";
void menu_download_file_connect_print(zxvision_window *w)
{
    char buf_temp[NETWORK_MAX_URL+256];
    sprintf(buf_temp,"Downloading from %s",menu_download_file_connect_print_host);

    //darle espacio para meter el caracter de progreso al final
    //-2 de lo habitual en ancho visible y -2 para poder agregar un espacio y el caracter de progreso
    int max_visible_width=w->visible_width-4;

    int longitud_texto=strlen(buf_temp);
    if (longitud_texto>max_visible_width) {
        buf_temp[max_visible_width]=0;
    }

	//menu_common_connect_print(w,"Downloading");
    menu_common_connect_print(w,buf_temp);
}

int menu_zeng_connect_cond(zxvision_window *w GCC_UNUSED)
{
	return !zeng_enable_thread_running;
}

void menu_zeng_enable_disable(MENU_ITEM_PARAMETERS)
{
	if (zeng_enabled.v) {
		zeng_disable();
	}
	else {

		//Activamos ZRCP, que es lo logico, si es que no esta habilitado ya
		if (remote_protocol_enabled.v==0) enable_and_init_remote_protocol();



		//Lanzar el thread de activacion
		zeng_enable();

		contador_menu_zeng_connect_print=0;


        strcpy(menu_zeng_connect_print_host,zeng_remote_hostname);
		zxvision_simple_progress_window("ZENG connection", menu_zeng_connect_cond,menu_zeng_connect_print );



		//menu_footer_bottom_line();
	}
}

int menu_zeng_enable_disable_cond(void)
{



	//Si esta habilitado, opcion siempre disponible para desactivar
	if (zeng_enabled.v) return 1;



	else {
		//Si esta hostname vacio
		if (zeng_remote_hostname[0]==0) return 0;
	}

	return 1;
}


//Si esta habilitado, no se puede cambiar parametro
int menu_zeng_host_cond(void)
{
	if (zeng_enabled.v) return 0;
	else return 1;
}

void menu_zeng_host(MENU_ITEM_PARAMETERS)
{

	menu_ventana_scanf("Remote hosts",zeng_remote_hostname,MAX_ZENG_HOSTNAME);

}


void menu_zeng_port(MENU_ITEM_PARAMETERS)
{


        char string_port[6];


        sprintf (string_port,"%d",zeng_remote_port);


	menu_ventana_scanf("Remote port",string_port,6);
	int numero=parse_string_to_number(string_port);
	if (numero<1 || numero>65535) {
		menu_error_message("Invalid port number");
		return;
	}

	zeng_remote_port=numero;


}

void menu_zeng_master(MENU_ITEM_PARAMETERS)
{
	zeng_i_am_master ^=1;
}


void menu_zeng_snapshot_frames(MENU_ITEM_PARAMETERS)
{

        //hasta valor de 9*50*20=9000
		char string_frames[5];

        //El valor se enseña al usuario en milisegundos, que es mas entendedor (creo) que frames de video
		sprintf (string_frames,"%d",zeng_frames_video_cada_snapshot*20);


		menu_ventana_scanf("Snapshot every ms?",string_frames,5);
		int numero=parse_string_to_number(string_frames);

		if (numero<20 || numero>9*50*20) {
			menu_error_message("Invalid interval");
			return;
		}


		zeng_frames_video_cada_snapshot=numero/20;

}


void menu_zeng_send_message(MENU_ITEM_PARAMETERS)
{
	char string_mensaje[AUTOSELECTOPTIONS_MAX_FOOTER_LENGTH];
	string_mensaje[0]=0;

	menu_ventana_scanf("Message?",string_mensaje,AUTOSELECTOPTIONS_MAX_FOOTER_LENGTH);

	zeng_add_pending_send_message_footer(string_mensaje);
}


int menu_zeng_send_message_cond(void)
{
	//Si hay un mensaje pendiente de enviar, no permitir aun
	//Comprobamos tambien que zeng_enabled.v, esto no se usa en menu pero si en tecla directa F
	if (zeng_enabled.v==0) return 0;
	if (pending_zeng_send_message_footer) return 0;

	return 1;
}

void menu_zeng_cancel_connect(MENU_ITEM_PARAMETERS)
{
	if (menu_confirm_yesno_texto("Still connecting","Cancel?")) {
		//printf ("cancelling zeng connect\n");
		zeng_cancel_connect();
	}
}


void menu_zeng_snapshot_force_reconnect(MENU_ITEM_PARAMETERS)
{
    zeng_force_reconnect_failed_retries.v ^=1;
}

void menu_zeng_do_not_send_input_events(MENU_ITEM_PARAMETERS)
{
    zeng_do_not_send_input_events ^=1;
}

char menu_zeng_manual_sync_snapshot_remote_hostname[MAX_ZENG_HOSTNAME]="";

void menu_zeng_manual_sync_snapshot_print(zxvision_window *w)
{
    char buf_temp[MAX_ZENG_HOSTNAME+256];
    sprintf(buf_temp,"Connecting to %s",menu_zeng_manual_sync_snapshot_remote_hostname);

    menu_common_connect_print(w,buf_temp);
}

int menu_zeng_manual_sync_snapshot_cond(zxvision_window *w GCC_UNUSED)
{
	return !zeng_utils_sync_local_to_remote_thread_running;
}

void menu_zeng_manual_sync_snapshot(MENU_ITEM_PARAMETERS)
{


    if (menu_ventana_scanf("Remote host?",menu_zeng_manual_sync_snapshot_remote_hostname,MAX_ZENG_HOSTNAME)<0) {
        return;
    }

    zeng_utils_sync_local_to_remote(menu_zeng_manual_sync_snapshot_remote_hostname);

    zxvision_simple_progress_window("Syncing snapshot", menu_zeng_manual_sync_snapshot_cond,menu_zeng_manual_sync_snapshot_print );
}

void menu_zeng(MENU_ITEM_PARAMETERS)
{
        //Dado que es una variable local, siempre podemos usar este nombre array_menu_common
        menu_item *array_menu_common;
        menu_item item_seleccionado;
        int retorno_menu;
        do {

			menu_add_item_menu_inicial(&array_menu_common,"",MENU_OPCION_UNASSIGNED,NULL,NULL);

			//Si esta thread de conexion ejecutandose, mostrar otra opcion
			if (zeng_enable_thread_running) {
				menu_add_item_menu_format(array_menu_common,MENU_OPCION_NORMAL,menu_zeng_cancel_connect,NULL,"    Connecting...");
			}

            else {
            	menu_add_item_menu_format(array_menu_common,MENU_OPCION_NORMAL,menu_zeng_enable_disable,menu_zeng_enable_disable_cond,"[%c] ~~Enabled",(zeng_enabled.v ? 'X' : ' ') );
				menu_add_item_menu_shortcut(array_menu_common,'e');
			}

			char string_host_shown[16];
			menu_tape_settings_trunc_name(zeng_remote_hostname,string_host_shown,16);
			menu_add_item_menu_format(array_menu_common,MENU_OPCION_NORMAL,menu_zeng_host,menu_zeng_host_cond,"Remote ~~Hosts [%s]",string_host_shown);
            menu_add_item_menu_prefijo(array_menu_common,"    ");
            menu_add_item_menu_tooltip(array_menu_common,"Remote hosts to connect to");
            menu_add_item_menu_ayuda(array_menu_common,"You may specify several hosts separated with comma (,) and no whitespace between them. "
                "You can also specify a different port using format host:port"
                );
			menu_add_item_menu_shortcut(array_menu_common,'h');


			menu_add_item_menu_format(array_menu_common,MENU_OPCION_NORMAL,menu_zeng_port,menu_zeng_host_cond,"Remote Port [%d]",zeng_remote_port);
            menu_add_item_menu_tooltip(array_menu_common,"Remote port to connect to; this port can be overriden in the previous Remote Hosts menu item "
                "if written host:port");
            menu_add_item_menu_ayuda(array_menu_common,"Remote port to connect to; this port can be overriden in the previous Remote Hosts menu item "
                "if written host:port");
            menu_add_item_menu_prefijo(array_menu_common,"    ");

			menu_add_item_menu_format(array_menu_common,MENU_OPCION_NORMAL,menu_zeng_master,menu_zeng_host_cond,"[%c] ~~Master",(zeng_i_am_master ? 'X' : ' ') );
			menu_add_item_menu_shortcut(array_menu_common,'m');

			if (zeng_i_am_master) {
				menu_add_item_menu_format(array_menu_common,MENU_OPCION_NORMAL,menu_zeng_snapshot_frames,NULL,"Snapshot freq [%d ms]",zeng_frames_video_cada_snapshot*20);
                menu_add_item_menu_prefijo(array_menu_common,"    ");
                menu_add_item_menu_tooltip(array_menu_common,"Snapshot sending frequency on miliseconds");
                menu_add_item_menu_ayuda(array_menu_common,"Snapshot sending frequency on miliseconds");

                menu_add_item_menu_format(array_menu_common,MENU_OPCION_NORMAL,menu_zeng_snapshot_force_reconnect,NULL,"[%c] Force reconnect",
                (zeng_force_reconnect_failed_retries.v ? 'X' : ' ' ));
                menu_add_item_menu_tooltip(array_menu_common,"Force reconnect when sending snapshot fails 3 times");
                menu_add_item_menu_ayuda(array_menu_common,"Force reconnect when sending snapshot fails 3 times");
			}

            menu_add_item_menu_format(array_menu_common,MENU_OPCION_NORMAL,menu_zeng_do_not_send_input_events,NULL,"[%c] Send input events",
                (zeng_do_not_send_input_events ? ' ' : 'X'));
            menu_add_item_menu_tooltip(array_menu_common,"Send input events (keyboard, joystick) to other hosts");
            menu_add_item_menu_ayuda(array_menu_common,"Send input events (keyboard, joystick) to other hosts");

			if (zeng_enabled.v) {
				menu_add_item_menu_format(array_menu_common,MENU_OPCION_NORMAL,menu_zeng_send_message,menu_zeng_send_message_cond,"Broadcast message");
                menu_add_item_menu_prefijo(array_menu_common,"    ");
                menu_add_item_menu_add_flags(array_menu_common,MENU_ITEM_FLAG_GENERA_VENTANA | MENU_ITEM_FLAG_SE_CERRARA);
			}

            menu_add_item_menu_separator(array_menu_common);

            menu_add_item_menu_format(array_menu_common,MENU_OPCION_NORMAL,menu_zeng_manual_sync_snapshot,NULL,"Sync snapshot to remote");
            menu_add_item_menu_prefijo(array_menu_common,"    ");
            menu_add_item_menu_add_flags(array_menu_common,MENU_ITEM_FLAG_GENERA_VENTANA | MENU_ITEM_FLAG_SE_CERRARA);


			menu_add_item_menu_separator(array_menu_common);

            menu_add_ESC_item(array_menu_common);

            retorno_menu=menu_dibuja_menu_no_title_lang(&zeng_opcion_seleccionada,&item_seleccionado,array_menu_common,"ZENG" );


                if ((item_seleccionado.tipo_opcion&MENU_OPCION_ESC)==0 && retorno_menu>=0) {
                        //llamamos por valor de funcion
                        if (item_seleccionado.menu_funcion!=NULL) {
                                //printf ("actuamos por funcion\n");
                                item_seleccionado.menu_funcion(item_seleccionado.valor_opcion);

                        }
                }

        } while ( (item_seleccionado.tipo_opcion&MENU_OPCION_ESC)==0 && retorno_menu!=MENU_RETORNO_ESC && !salir_todos_menus);




}

int menu_online_zx81_letra(char filtro,char letra)
{
	letra=letra_minuscula(letra);
	filtro=letra_minuscula(filtro);
	if (filtro>='a' && filtro<='z') {
		if (letra==filtro) return 1;
		else return 0;
	}
	else {
		//todo lo que no son letras
		if (letra<'a' || letra>'z') return 1;
		else return 0;
	}
}

char online_browse_zx81_ultima_letra='a';

char menu_online_browse_zx81_letter(void)
{

	menu_espera_no_tecla();
	menu_reset_counters_tecla_repeticion();

	zxvision_window ventana;

	int ancho_ventana=23;
	int alto_ventana=8;

	int xventana=menu_center_x()-ancho_ventana/2;
	int yventana=menu_center_y()-alto_ventana/2;

	char letra_seleccionada=0;

	zxvision_new_window(&ventana,xventana,yventana,ancho_ventana,alto_ventana,
							ancho_ventana-1,alto_ventana-2,"Initial letter");
	zxvision_draw_window(&ventana);


    menu_item *array_menu_osd_adventure_keyboard;
    menu_item item_seleccionado;
    int retorno_menu;
    int salir=0;
    do {




        //Como no sabemos cual sera el item inicial, metemos este sin asignar
        menu_add_item_menu_inicial(&array_menu_osd_adventure_keyboard,"",MENU_OPCION_UNASSIGNED,NULL,NULL);

	//if (osd_adv_kbd_list[adventure_keyboard_selected_item][adventure_keyboard_index_selected_item]==0) {
	//osd_adv_kbd_defined
		//int i;
		int last_x=4;
		int last_y=0;
		char letra='a';
        int nletra=0;


		for (;letra<='z'+1;letra++) {
			char letra_mostrar=letra;
			if (letra=='z'+1) letra_mostrar='#';
		    menu_add_item_menu_format(array_menu_osd_adventure_keyboard,MENU_OPCION_NORMAL,menu_osd_adventure_keyboard_action,NULL,"%c",letra_mostrar);
            menu_add_item_menu_tabulado(array_menu_osd_adventure_keyboard,last_x,last_y);
            menu_add_item_menu_valor_opcion(array_menu_osd_adventure_keyboard,letra_mostrar);

            menu_add_item_menu_shortcut(array_menu_osd_adventure_keyboard,letra_mostrar);

			last_x +=3;
            nletra++;
			if (nletra==5) {
				last_x=4;
				last_y++;
                nletra=0;
			}
		}



		//Nombre de ventana solo aparece en el caso de stdout
        retorno_menu=menu_dibuja_menu_no_title_lang(&online_browse_zx81_letter_opcion_seleccionada,&item_seleccionado,array_menu_osd_adventure_keyboard,"Initial letter" );


	    //En caso de menus tabulados, es responsabilidad de este de borrar la ventana

                if ((item_seleccionado.tipo_opcion&MENU_OPCION_ESC)==0 && retorno_menu>=0) {
                        //llamamos por valor de funcion
                        if (item_seleccionado.menu_funcion!=NULL) {
				//printf ("Item seleccionado: %d\n",item_seleccionado.valor_opcion);
                                //printf ("actuamos por funcion\n");



                                letra_seleccionada=item_seleccionado.valor_opcion;

				salir=1;

                        }
                }

        } while ( (item_seleccionado.tipo_opcion&MENU_OPCION_ESC)==0 && retorno_menu!=MENU_RETORNO_ESC && !salir_todos_menus && !salir);






		//printf ("en final de funcion\n");
		zxvision_destroy_window(&ventana);

	return letra_seleccionada;


}

void menu_online_browse_zx81_create_menu(char *mem, char *mem_after_headers,int total_leidos,char letra,char *juego,char *url_juego)
{

	//Por defecto
	url_juego[0]=0;
	juego[0]=0;

	//Dado que es una variable local, siempre podemos usar este nombre array_menu_common
    menu_item *array_menu_common;
    menu_item item_seleccionado;
    int retorno_menu;

	menu_add_item_menu_inicial(&array_menu_common,"",MENU_OPCION_UNASSIGNED,NULL,NULL);

	//temp limite
	char texto_final[30000];

	int total_items=0;

    int indice_destino=0;

    int dif_header=mem_after_headers-mem;
    total_leidos -=dif_header;
    mem=mem_after_headers;

    //leer linea a linea
    char buffer_linea[1024];
    int i=0;
    int salir=0;
    do {
        int leidos;
        char *next_mem;

        //printf("Restantes total_leidos antes de leer linea: %d\n",total_leidos);
        next_mem=util_read_line(mem,buffer_linea,total_leidos,1024,&leidos);
        //printf("Linea leida: [%s]\n",buffer_linea);
        total_leidos -=leidos;
        //printf("Restantes total_leidos despues de leer linea: %d\n",total_leidos);

        //Nota: antes se salia al leer una linea en blanco pero en algun momento del año 2024
        //metieron una linea en blanco justo al principio (despues de <html>) y falló esto
        /*
        if (buffer_linea[0]==0) {
            salir=1;
            //printf ("salir con linea vacia final\n");
            mem=next_mem;
        }

        else {
        */
        //printf ("cabecera %d: %s\n",i,buffer_linea);
        //ver si contiene texto de juego

        char *existe;
        existe=strstr(buffer_linea,"/files/");
        if (existe!=NULL) {
            if (menu_online_zx81_letra(letra,existe[7])) {
            //if (existe[7]==letra) {
                //quitar desde comilla derecha
                char *comilla;
                comilla=strstr(&existe[7],"\"");
                if (comilla!=NULL) *comilla=0;
                debug_printf (VERBOSE_PARANOID,"Adding raw html line %s",buffer_linea);
                //Todo controlar maximo buffer y maximo que puede mostrar ventana
                sprintf(&texto_final[indice_destino],"%s\n",&existe[7]);
                indice_destino +=strlen(&existe[7])+1;

                menu_add_item_menu_format(array_menu_common,MENU_OPCION_NORMAL,NULL,NULL,&existe[7]);
                debug_printf (VERBOSE_DEBUG,"Adding menu entry %s",&existe[7]);
                total_items++;
            }
        }
        i++;
        mem=next_mem;
        //}

        //Salir al llegar al final de lo leido
        if (total_leidos<=0) salir=1;

    } while (!salir);

    texto_final[indice_destino]=0;



    menu_add_item_menu_separator(array_menu_common);

    menu_add_ESC_item(array_menu_common);

    if (total_items) {
        //Si hay resultados con esa letra, normalmente si..
        retorno_menu=menu_dibuja_menu_dialogo_no_title_lang(&zx81_online_browser_opcion_seleccionada,&item_seleccionado,array_menu_common,"ZX81 Games" );


        if ((item_seleccionado.tipo_opcion&MENU_OPCION_ESC)==0 && retorno_menu>=0) {
            //que juego se ha seleccionado

            //item_seleccionado.menu_funcion(item_seleccionado.valor_opcion);
            //char *juego;
            strcpy(juego,item_seleccionado.texto_opcion);
            debug_printf (VERBOSE_INFO,"Selected game: %s",juego);

            sprintf(url_juego,"/files/%s",juego);
        }
    }

    else {
        menu_error_message("No results found");
    }


}

void get_online_download_path(char *ruta)
{
    if (online_download_path[0]!=0) strcpy(ruta,online_download_path);
    else sprintf(ruta,"%s/download",get_tmpdir_base());
}

void menu_online_browse_zx81(MENU_ITEM_PARAMETERS)
{

#ifdef NETWORKING_DISABLED
    //En caso que no haya soporte de red compilado
    //Aqui se puede llegar desde acceso directo de icono
    menu_error_message("You need to have networking compiled to browse zx81 speccy games");
    return;
#endif


	do {
		//char oldletra=s_online_browse_zx81_letra[0];

		//menu_ventana_scanf("Letter",s_online_browse_zx81_letra,2);

		//char letra=s_online_browse_zx81_letra[0];

		char letra=menu_online_browse_zx81_letter();
		if (letra==0) return;

	    stats_total_zx81_browser_queries++;

		//printf ("old letra %c new letra %c\n",online_browse_zx81_ultima_letra,letra);

		//si cambia letra, poner cursor arriba
		if (letra!=online_browse_zx81_ultima_letra) zx81_online_browser_opcion_seleccionada=0;

		online_browse_zx81_ultima_letra=letra;


		int http_code;
		char *mem;
		char *orig_mem;
		char *mem_after_headers;
		int total_leidos;
		char redirect_url[NETWORK_MAX_URL];


		int retorno=menu_zsock_http("www.zx81.nl","/files.html",&http_code,&mem,&total_leidos,&mem_after_headers,1,"",0,redirect_url,"");
		orig_mem=mem;

		//printf("%s\n",mem);

		if (mem_after_headers!=NULL && http_code==200) {
				char url_juego[1024];
				char juego[MAX_TEXTO_OPCION];

				menu_online_browse_zx81_create_menu(mem, mem_after_headers,total_leidos,letra,juego,url_juego);

				if (url_juego[0]!=0) {

                	//cargar
                	char archivo_temp[PATH_MAX];
					//sprintf (archivo_temp,"/tmp/%s",juego);
					//sprintf (archivo_temp,"%s/%s",get_tmpdir_base(),juego);
                    char tempdir[PATH_MAX];
                    get_online_download_path(tempdir);

                    menu_filesel_mkdir(tempdir);
                    sprintf (archivo_temp,"%s/%s",tempdir,juego);


					//usamos misma funcion thread que usa download wos y otros
					int ret=menu_download_file("www.zx81.nl",url_juego,archivo_temp,0,1024*1024,"");  //1 MB mas que suficiente

					if (ret==200) {

  						//y cargar
  						strcpy(quickload_file,archivo_temp);

						quickfile=quickload_file;

                        //Creamos enlace directo en escritorio
                        zxvision_create_configurable_icon_file_type(F_FUNCION_DESKTOP_SNAPSHOT,quickload_file);

						if (quickload(quickload_file)) {
							debug_printf (VERBOSE_ERR,"Unknown file format");
						}

						//Agregar a ultimos archivos usados
						last_filesused_insert(quickload_file);

						//Y salir todos menus
						salir_todos_menus=1;
					}


					else {
						//debug_printf(VERBOSE_ERR,"Error downloading game. Return code: %d",ret);

						if (ret<0) {
							//printf ("Error: %d %s\n",retorno,z_sock_get_error(retorno));
							menu_network_error(ret);
						}
						else {
							debug_printf(VERBOSE_ERR,"Error downloading. Return code: %d",ret);
						}

					}
				}

		}
		//Fin resultado http correcto
		else {
			if (retorno<0) {
				debug_printf (VERBOSE_DEBUG,"Error: %d %s",retorno,z_sock_get_error(retorno));
				menu_network_error(retorno);
			}
			else {
				debug_printf(VERBOSE_ERR,"Error downloading. Return code: %d",http_code);
			}
		}


		if (orig_mem!=NULL) free(orig_mem);

	} while (!salir_todos_menus);
	//Se saldra al seleccionar juego o al pulsar ESC desde seleccion letra (ahi se sale con return tal cual)

}




struct menu_zsock_http_struct
{

	char *host;
	char *url;
	int *http_code;
	char **mem;
	int *t_leidos;
	char **mem_after_headers;
	int skip_headers;
	char *add_headers;
	int use_ssl;
	char *redirect_url;
    char *ssl_sni_host_name;


	int return_code;

};

int menu_zsock_http_thread_running=0;

int menu_menu_zsock_http_cond(zxvision_window *w GCC_UNUSED)
{
	return !menu_zsock_http_thread_running;
}



void *menu_menu_zsock_http_thread_function(void *entrada)
{

	//menu_zsock_http_thread_running=1;

#ifndef NETWORKING_DISABLED

	debug_printf (VERBOSE_DEBUG,"Starting zsock http thread. Host=%s Url=%s",
								((struct menu_zsock_http_struct *)entrada)->host,
								((struct menu_zsock_http_struct *)entrada)->url);

//((struct menu_zsock_http_struct *)entrada)->return_code=-1;


	((struct menu_zsock_http_struct *)entrada)->return_code=
			zsock_http(
								((struct menu_zsock_http_struct *)entrada)->host,
								((struct menu_zsock_http_struct *)entrada)->url,
								((struct menu_zsock_http_struct *)entrada)->http_code,
								((struct menu_zsock_http_struct *)entrada)->mem,
								((struct menu_zsock_http_struct *)entrada)->t_leidos,
								((struct menu_zsock_http_struct *)entrada)->mem_after_headers,
								((struct menu_zsock_http_struct *)entrada)->skip_headers,
								((struct menu_zsock_http_struct *)entrada)->add_headers,
								((struct menu_zsock_http_struct *)entrada)->use_ssl,
								((struct menu_zsock_http_struct *)entrada)->redirect_url,
								0,
                                ((struct menu_zsock_http_struct *)entrada)->ssl_sni_host_name
							);



	debug_printf (VERBOSE_DEBUG,"Finishing zsock http thread");

#endif
	menu_zsock_http_thread_running=0;

	return 0;

}

#ifdef USE_PTHREADS
pthread_t menu_zsock_http_thread;
#endif

int menu_zsock_http(char *host, char *url,int *http_code,char **mem,int *t_leidos, char **mem_after_headers,
            int skip_headers,char *add_headers,int use_ssl,char *redirect_url,char *ssl_sni_host_name)
{


	//Lanzar el thread de descarga
	struct menu_zsock_http_struct parametros;

	parametros.host=host;
	parametros.url=url;
	parametros.http_code=http_code;
	parametros.mem=mem;
	parametros.t_leidos=t_leidos;
	parametros.mem_after_headers=mem_after_headers;
	parametros.skip_headers=skip_headers;
	parametros.add_headers=add_headers;
	parametros.use_ssl=use_ssl;
	parametros.redirect_url=redirect_url;
    parametros.ssl_sni_host_name=ssl_sni_host_name;

	//de momento not found y error, y mem a null
	parametros.return_code=-1;
	*(parametros.http_code)=404;
	*(parametros.mem)=NULL;
	*(parametros.mem_after_headers)=NULL;
	*(parametros.t_leidos)=0;
	parametros.redirect_url[0]=0;


#ifndef NETWORKING_DISABLED

	//Inicializar thread
	debug_printf (VERBOSE_DEBUG,"Initializing thread menu_menu_zsock_http_thread_function");


	//Antes de lanzarlo, decir que se ejecuta, por si el usuario le da enter rapido a la ventana de progreso y el thread aun no se ha lanzado
	menu_zsock_http_thread_running=1;

	if (pthread_create( &menu_zsock_http_thread, NULL, &menu_menu_zsock_http_thread_function, (void *)&parametros) ) {
		debug_printf(VERBOSE_ERR,"Can not create zsock http thread");
		return -1;
	}

	//y pthread en estado detached asi liberara su memoria asociada a thread al finalizar, sin tener que hacer un pthread_join
	pthread_detach(menu_zsock_http_thread);

#endif


	contador_menu_zeng_connect_print=0;

	//Usamos misma ventana de progreso que zeng. TODO: si se lanzan los dos a la vez (cosa poco probable) se moverian uno con el otro
    strcpy(menu_zeng_connect_print_host,host);
	zxvision_simple_progress_window("Downloading", menu_menu_zsock_http_cond,menu_zeng_connect_print );

	//TODO Si antes de finalizar la descarga se vuelve atras y se vuelve a realizar otra busqueda, puede dar problemas
	//ya que la variable menu_zsock_http_thread_running es global y única

	if (menu_zsock_http_thread_running) menu_warn_message("Download has not ended yet");

	//despues de mostrar el aviso, si la tarea sigue en ejecucion, retornamos error 404
	if (menu_zsock_http_thread_running) return 404;

	return parametros.return_code;

}


struct download_wos_struct
{
	char *host;
	char *url;
	char *archivo_temp;
	int ssl_use;
	int return_code;
	int estimated_maximum_size;
    char *ssl_sni_host_name;
};

int download_wos_thread_running=0;

int menu_download_file_cond(zxvision_window *w GCC_UNUSED)
{
	return !download_wos_thread_running;
}



void *menu_download_file_thread_function(void *entrada)
{

	//download_wos_thread_running=1;

#ifndef NETWORKING_DISABLED

	debug_printf (VERBOSE_DEBUG,"Starting download content thread. Host=%s Url=%s",
	((struct download_wos_struct *)entrada)->host,
								((struct download_wos_struct *)entrada)->url);

	((struct download_wos_struct *)entrada)->return_code=util_download_file( ((struct download_wos_struct *)entrada)->host,
								((struct download_wos_struct *)entrada)->url,
								((struct download_wos_struct *)entrada)->archivo_temp,
								((struct download_wos_struct *)entrada)->ssl_use,
								((struct download_wos_struct *)entrada)->estimated_maximum_size,
                                ((struct download_wos_struct *)entrada)->ssl_sni_host_name);

	debug_printf (VERBOSE_DEBUG,"Finishing download content thread");

#endif
	download_wos_thread_running=0;

	return 0;

}

#ifdef USE_PTHREADS
pthread_t download_wos_thread;
#endif





//antes llamado menu_download_wos
int menu_download_file(char *host,char *url,char *archivo_temp,int ssl_use,int estimated_maximum_size,char *ssl_sni_host_name)
{


	//Lanzar el thread de descarga
	struct download_wos_struct parametros;

	parametros.host=host;
	parametros.url=url;
	parametros.archivo_temp=archivo_temp;
	parametros.ssl_use=ssl_use;
	parametros.estimated_maximum_size=estimated_maximum_size;
    parametros.ssl_sni_host_name=ssl_sni_host_name;

	//de momento not found
	parametros.return_code=404;


#ifndef NETWORKING_DISABLED

	//Inicializar thread

	//Antes de lanzarlo, decir que se ejecuta, por si el usuario le da enter rapido a la ventana de progreso y el thread aun no se ha lanzado
	download_wos_thread_running=1;

	if (pthread_create( &download_wos_thread, NULL, &menu_download_file_thread_function, (void *)&parametros) ) {
		debug_printf(VERBOSE_ERR,"Can not create download wos thread");
		return -1;
	}

	//y pthread en estado detached asi liberara su memoria asociada a thread al finalizar, sin tener que hacer un pthread_join
	pthread_detach(download_wos_thread);

#endif


	contador_menu_zeng_connect_print=0;

	//Usamos misma ventana de progreso que zeng. TODO: si se lanzan los dos a la vez (cosa poco probable) se moverian uno con el otro
    strcpy(menu_download_file_connect_print_host,host);
	zxvision_simple_progress_window("Downloading software", menu_download_file_cond,menu_download_file_connect_print );

	//TODO Si antes de finalizar la descarga se vuelve atras y se vuelve a realizar otra busqueda, puede dar problemas
	//ya que la variable download_wos_thread_running es global y única

	if (download_wos_thread_running) menu_warn_message("Download has not ended yet");

	//despues de mostrar el aviso, si la tarea sigue en ejecucion, retornamos error 404
	if (download_wos_thread_running) return 404;

	return parametros.return_code;

}
/*
Obtiene nombre de juego de manera inteligente, segun su nombre de archivo
Se trata de quitar extension conocida
Ejemplo: ChaseH.Q..tzx.zip
Debe obtener: ChaseH.Q.
*/
void menu_online_browse_intelli_get_name(char *nombre_origen,char *nombre_final)
{
    //util_get_file_without_extension(nombre_sin_dir,nombre_sin_ext);
    //Si nombre acaba en cualquiera de:
    //tap.zip
    //dsk.zip
    //tzx.zip
    //Quitar esa doble extension
    //Cualquier otra, dejar tal cual
    char *posibles_extensiones[]={
        ".tap.zip",
        ".dsk.zip",
        ".tzx.zip",
        ".z80.zip",
        ".sna.zip",
        ".rom.zip",
        ".trd.zip",
        ".mdr.zip",

        //hay otras extensiones pero como no las soporta smartload, mejor mostrar extension
        //.mgt.zip
        //.slt.zip


        //Indica final de array
        ""
    };

    int i;
    char *encontrado=NULL;


    for (i=0;posibles_extensiones[i][0] && encontrado==NULL;i++) {
        encontrado=strstr(nombre_origen,posibles_extensiones[i]);
    }

    //de momento copiar tal cual
    strcpy(nombre_final,nombre_origen);

    if (encontrado!=NULL) {
        //longitud hasta encontrada coincidencia. Restamos los dos punteros
        int longitud_nombre_sin_extension=encontrado-nombre_origen;
        //Acortar el texto
        nombre_final[longitud_nombre_sin_extension]=0;
    }


}


//showindex dice si muestra contenido texto variable index en el item->usado para mostrar el archivo de la url
//en las diferentes descargas de un mismo juego
void menu_online_browse_zxinfowos_query(char *query_result,char *hostname,char *query_url,char *preffix,
    char *string_index,char *string_display,
     char *add_headers,int showindex,char *windowtitle,char *error_not_found_message)
{

	//Por defecto
	query_result[0]=0;

	//Dado que es una variable local, siempre podemos usar este nombre array_menu_common
	menu_item *array_menu_common;
	menu_item item_seleccionado;
	int retorno_menu;

	int zxinfo_wos_opcion_seleccionada=0;
	do {

		menu_add_item_menu_inicial(&array_menu_common,"",MENU_OPCION_UNASSIGNED,NULL,NULL);


		int http_code;
		char *mem;
		char *orig_mem;
		char *mem_after_headers;
		int total_leidos;


		char redirect_url[NETWORK_MAX_URL];


		int retorno=menu_zsock_http(hostname,query_url,&http_code,&mem,&total_leidos,&mem_after_headers,1,add_headers,0,redirect_url,"");



		orig_mem=mem;

		if (mem_after_headers!=NULL && http_code==200) {

			int dif_header=mem_after_headers-mem;
			total_leidos -=dif_header;
			mem=mem_after_headers;

			//leer linea a linea
			char buffer_linea[1024];
			int i=0;
			//int salir=0;

			int existe_id;
			int existe_fulltitle;
			int ultimo_indice_id;
			int ultimo_indice_fulltitle;

			char ultimo_id[1024];
			char ultimo_fulltitle[1024];

			existe_id=0;
			existe_fulltitle=0;
			ultimo_id[0]=0;
			ultimo_fulltitle[0]=0;
			ultimo_indice_id=0;
			ultimo_indice_fulltitle=0;

			int total_items=0;

			//Leer linea a linea la respuesta http, y meterlo en items de menu
			do {
				int leidos;
				char *next_mem;

				next_mem=util_read_line(mem,buffer_linea,total_leidos,1024,&leidos);
                debug_printf(VERBOSE_DEBUG,"menu_online_browse_zxinfowos_query: read line: [%s]",buffer_linea);
				total_leidos -=leidos;

				//if (buffer_linea[0]==0) {
				//	salir=1;
				//	//printf ("salir con linea vacia final\n");
				//	mem=next_mem;
				//}
				//else {
                //Anteriormente se detenia al encontrar una linea en blanco pero esto es un error,
                //creo que venia de codigo heredado del browser de zx81 online en que ahi si que
                //tiene sentido detenerse en ese caso (o no?)

                //printf ("cabecera %d: %s\n",i,buffer_linea);
                //ver si contine texto de juego

                /*
                Campos a buscar:

hits.0._id=0002258
hits.0.title=Headcoach

Pueden salir antes id o antes title. En bucle leer los dos y cuando estén los dos y tengan mismo .n., agregar a menu
                */

                //filtrar antes los que tienen prefijo
                char *existe_prefijo;

                existe_prefijo=strstr(buffer_linea,preffix);
                if (existe_prefijo!=NULL) {

                    char *existe;
                    existe=strstr(buffer_linea,string_index); //"_id=");
                    if (existe!=NULL) {
                            int pos=strlen(string_index);
                            //printf("id: existe_id %d existe_fulltitle %d buffer_linea: %s\n",existe_id,existe_fulltitle,buffer_linea);
                            strcpy(ultimo_id,&existe[pos]);
                            existe_id=1;
                            char *existe_indice;
                            existe_indice=strstr(buffer_linea,preffix);
                            if (existe_indice!=NULL) {
                                //saltar el prefijo para obtener el numero
                                int l=strlen(preffix);
                                ultimo_indice_id=parse_string_to_number(&existe_indice[l]);
                            }
                    }

                    existe=strstr(buffer_linea,string_display); //"fulltitle=");
                    if (existe!=NULL) {
                        //hay que descartar entradas tipo hits.0.screens.1.title=null
                        //que hacen confundir y pensar que son entradas de title
                        //TODO: esta exclusion realmente solo haria falta en la primera llamada aqui,
                        //en la busqueda de juegos. En cambio en la de game details no hace falta,
                        //pero bueno tampoco molesta y por no complicar mas el código, lo dejamos
                        char *existe_screen=strstr(buffer_linea,".screens.");
                        if (existe_screen==NULL) {

                            int pos=strlen(string_display);
                            //printf("ti: existe_id %d existe_fulltitle %d buffer_linea: %s\n",existe_id,existe_fulltitle,buffer_linea);
                            strcpy(ultimo_fulltitle,&existe[pos]);
                            existe_fulltitle=1;
                            char *existe_indice;
                            existe_indice=strstr(buffer_linea,preffix);
                            if (existe_indice!=NULL) {
                                //saltar el prefijo para obtener el numero
                                int l=strlen(preffix);
                                ultimo_indice_fulltitle=parse_string_to_number(&existe_indice[l]);
                            }

                        }
                    }

                    if (existe_id && existe_fulltitle) {
                        //printf("ultimo_indice_id %d ultimo_indice_fulltitle %d\n",ultimo_indice_id,ultimo_indice_fulltitle);
                        if (ultimo_indice_id==ultimo_indice_fulltitle) {

                            //printf ("Adding menu item [%s] id [%s]\n",ultimo_fulltitle,ultimo_id);
                            debug_printf (VERBOSE_DEBUG,"Adding menu item [%s] id [%s]",ultimo_fulltitle,ultimo_id);

                            //meter en entrada linea indice. Realmente para que la queremos?
                            //solo la muestro en la busqueda inicial, en la seleccion del formato de archivo ya no
                            if (!showindex) {
                                //Remodificamos ultimo_fulltitle para meterle el indice delante
                                char buf[1024];
                                sprintf (buf,"%2d %s",ultimo_indice_id,ultimo_fulltitle);
                                strcpy(ultimo_fulltitle,buf);
                            }


                            //controlar maximo 30 caracteres
                            //TODO: si hacemos que se guarde geometria de ventana, teniendo ancho mayor que 32, esta maximo podria ser el ancho
                            //maximo que permite un item de menu (MAX_TEXTO_OPCION)
                            ultimo_fulltitle[30]=0;


                            //TODO controlar maximo items en menu. De momento esta limitado por la query a la api (100)
                            //Porque? realmente no hay un limite como tal en items de menu, no?

                            if (!showindex) {
                                menu_add_item_menu_format(array_menu_common,MENU_OPCION_NORMAL,NULL,NULL,ultimo_fulltitle);
                                menu_add_item_menu_misc(array_menu_common,ultimo_id);
                            }
                            else {
                                //printf ("%s\n",ultimo_id);
                                //obtener archivo sin extension de la descarga
                                char nombre_sin_dir[PATH_MAX];
                                char nombre_sin_ext[PATH_MAX];

                                util_get_file_no_directory(ultimo_id,nombre_sin_dir);

                                //Pillamos el nombre sin extension
                                menu_online_browse_intelli_get_name(nombre_sin_dir,nombre_sin_ext);

                                //Acortar el nombre por si acaso
                                char nombre_shown[28];

                                //strcpy(nombre_sin_ext,"01234567890123456789012345678901234567890123456789");

                                menu_tape_settings_trunc_name(nombre_sin_ext,nombre_shown,28);
                                //printf ("%s\n",nombre_sin_ext);

                                menu_add_item_menu_format(array_menu_common,MENU_OPCION_NORMAL,NULL,NULL,nombre_shown);
                                menu_add_item_menu_misc(array_menu_common,ultimo_id);

                                menu_add_item_menu_format(array_menu_common,MENU_OPCION_SEPARADOR,NULL,NULL," %s",ultimo_fulltitle);
                            }

                            total_items++;
                        }

                        existe_id=0;
                        existe_fulltitle=0;
                        ultimo_id[0]=0;
                        ultimo_fulltitle[0]=0;
                        ultimo_indice_id=0;
                        ultimo_indice_fulltitle=0;

                    }
                }

                i++;
                mem=next_mem;


				//if (total_leidos<=0) salir=1;

			//} while (!salir);
            } while (total_leidos>0);

			//texto_final[indice_destino]=0;
			if (orig_mem!=NULL) free(orig_mem);



			menu_add_item_menu_separator(array_menu_common);

			menu_add_ESC_item(array_menu_common);

			if (total_items) {

				retorno_menu=menu_dibuja_menu_dialogo_no_title_lang(&zxinfo_wos_opcion_seleccionada,&item_seleccionado,array_menu_common,
                    windowtitle );


				if ((item_seleccionado.tipo_opcion&MENU_OPCION_ESC)==0 && retorno_menu>=0) {

					//printf ("actuamos por funcion\n");
					//item_seleccionado.menu_funcion(item_seleccionado.valor_opcion);
					char *juego;
					juego=item_seleccionado.texto_opcion;

					char *url;
					url=item_seleccionado.texto_misc;
					debug_printf (VERBOSE_INFO,"Game [%s] url/id [%s]",juego,url);

					strcpy(query_result,url);
					return;

				}
			}

			else {
				//menu_error_message("No results found");
				menu_error_message(error_not_found_message);
				return;
			}
		}  //Aqui cierra mem_after_headers!=NULL && http_code==200

			//Fin resultado http correcto
		else {
			if (retorno<0) {
				//printf ("Error: %d %s\n",retorno,z_sock_get_error(retorno));
				menu_network_error(retorno);
				return;
			}
			else {
				debug_printf(VERBOSE_ERR,"Error downloading. Return code: %d",http_code);
				return;
			}
		}


    } while ( (item_seleccionado.tipo_opcion&MENU_OPCION_ESC)==0 && retorno_menu!=MENU_RETORNO_ESC && !salir_todos_menus);



}

void menu_zxinfo_get_final_url(char *url_orig,char *host_final,char *url_final,int *ssl_use)
{
	    /*  Local file links starting with /zxdb/sinclair/ refer to content added afterwards.
		These files are currently stored at https://spectrumcomputing.co.uk/zxdb/sinclair/  */

		/*
		Local file links starting with /pub/sinclair/ refer to content previously available at the original WorldOfSpectrum archive.
		These files are currently accessible from Archive.org mirror at
		https://archive.org/download/World_of_Spectrum_June_2017_Mirror/World%20of%20Spectrum%20June%202017%20Mirror.zip/World%20of%20Spectrum%20June%202017%20Mirror/sinclair/
		Local file links starting with /zxdb/sinclair/ refer to content added afterwards.
		These files are currently stored at https://spectrumcomputing.co.uk/zxdb/sinclair/


		https://github.com/zxdb/ZXDB/blob/master/README.md
		*/


#ifdef COMPILE_SSL
		*ssl_use=1;
		char *pref_wos="/pub/sinclair/";
		//char *pref_zxdb="/zxdb/sinclair/";

		if (strstr(url_orig,pref_wos)!=NULL) {
			debug_printf (VERBOSE_DEBUG,"WOS preffix");

			//Quitar /pub/sinclair
			char url_modif[NETWORK_MAX_URL];
			strcpy(url_modif,url_orig);

			int longitud_pref=strlen(pref_wos);
			//int longitud_url=strlen(url_orig);

			//longitud_url -=longitud_pref;
			//url_modif[longitud_url]=0;

			char *puntero_url;
			puntero_url=&url_modif[longitud_pref];

			//printf ("url modificada primero: %s\n",puntero_url);

			strcpy(host_final,"archive.org");
			sprintf(url_final,"/download/World_of_Spectrum_June_2017_Mirror/World%%20of%%20Spectrum%%20June%%202017%%20Mirror.zip/World%%20of%%20Spectrum%%20June%%202017%%20Mirror/sinclair/%s",puntero_url);
			debug_printf (VERBOSE_DEBUG,"Final URL: %s",url_final);

		}

		else {
			debug_printf (VERBOSE_DEBUG,"Spectrumcomputing preffix");
			//Asumimos que es zxdb
			strcpy(host_final,"spectrumcomputing.co.uk");
			strcpy(url_final,url_orig);
		}
#else
		//Si no tenemos ssl, solo podemos descargar contenido de wos tal cual
		debug_printf (VERBOSE_DEBUG,"Trying to download from WOS using HTTP as we don't have SSL support compiled in");
		*ssl_use=0;
		strcpy(host_final,"www.worldofspectrum.org");
		strcpy(url_final,url_orig);
#endif



}

char zxinfowos_query_search[256]="";

void menu_online_browse_zxinfowos(MENU_ITEM_PARAMETERS)
{

#ifndef COMPILE_SSL
    menu_error_message("You need to have SSL compiled to browse online speccy games");
    return;

    //Este first aid ya no se usa
	//menu_first_aid("no_ssl_wos");
#endif


#ifdef NETWORKING_DISABLED
    //En caso que no haya soporte de red compilado
    //Aqui se puede llegar desde acceso directo de icono
    menu_error_message("You need to have networking compiled to browse online speccy games");
    return;
#endif

    menu_first_aid("search_zxinfo");

	menu_ventana_scanf("Query",zxinfowos_query_search,256);
	if (zxinfowos_query_search[0]==0) return;

    stats_total_speccy_browser_queries++;



	//TODO podria pasar que al normalizar ocupe mas de 1024, pero la cadena de entrada tendria que ser muy grande
	char query_search_normalized[1024];

	util_normalize_query_http(zxinfowos_query_search,query_search_normalized);


	//http://a.zxinfo.dk/api/zxinfo/v2/search?query=head%20over%20heels&mode=compact&sort=rel_desc&size=10&offset=0

	do {
		char query_url[1024];

        //Old V2
		//sprintf (query_url,"/api/zxinfo/v2/search?query=%s&mode=compact&sort=rel_desc&size=100&offset=0&contenttype=SOFTWARE",query_search_normalized);

        //New V3
        sprintf (query_url,"/v3/search?query=%s&mode=compact&sort=rel_desc&size=100&offset=0&contenttype=SOFTWARE&output=flat",query_search_normalized);


		char query_id[256];

        //Old V2
		//menu_online_browse_zxinfowos_query(query_id,"a.zxinfo.dk",query_url,"hits.","_id=","fulltitle=","",0,"Spectrum games","No results found");
        //New V3 http://v3.zxinfo.dk
        menu_online_browse_zxinfowos_query(query_id,"v3.zxinfo.dk",query_url,"hits.","_id=","title=","",0,"Spectrum games","No results found");

		//gestionar resultado vacio
		if (query_id[0]==0) {
			//TODO resultado con ESC
			return;
		}

		debug_printf (VERBOSE_DEBUG,"Entry id result: %s",query_id);


		//http://a.zxinfo.dk/api/zxinfo/games/0002259?mode=compact

		/*
		releases.1.as_title=Foot and Mouth
	releases.1.releaseprice=£7.95
	releases.1.url=/pub/sinclair/games/h/HeadOverHeels.tap.zip
	releases.1.type=Tape image
		*/

        //Old V2
		//sprintf (query_url,"/api/zxinfo/games/%s?mode=compact",query_id);

        //New V3
		sprintf (query_url,"/v3/games/%s?mode=compact&output=flat",query_id);


        //New v3 http://v3.zxinfo.dk/v3/games/0000438?mode=compact&output=flat

		//Old V2
		//menu_online_browse_zxinfowos_query(query_id,"a.zxinfo.dk",query_url,"releases.","url=","format=","",1,"Releases",
		//									"No results found. Maybe there are no releases available or the game is copyright protected");

		//New V3
		menu_online_browse_zxinfowos_query(query_id,"v3.zxinfo.dk",query_url,"releases.","path=","format=","",1,"Releases",
											"No results found. Maybe there are no releases available or the game is copyright protected");


		//gestionar resultado vacio
		if (query_id[0]==0) {
			//TODO resultado con ESC
			return;
		}

		//gestionar resultado no vacio
		if (query_id[0]!=0) {
			// resultado no ESC


			debug_printf (VERBOSE_DEBUG,"Entry url result: %s",query_id);

			char url_juego[1024];
			sprintf(url_juego,"%s",query_id);
			//cargar
			char archivo_temp[PATH_MAX];


			/* Local file links starting with /zxdb/sinclair/ refer to content added afterwards.
			These files are currently stored at https://spectrumcomputing.co.uk/zxdb/sinclair/  */

			/*
			Local file links starting with /pub/sinclair/ refer to content previously available at the original WorldOfSpectrum archive.
			These files are currently accessible from Archive.org mirror at
			https://archive.org/download/World_of_Spectrum_June_2017_Mirror/World%20of%20Spectrum%20June%202017%20Mirror.zip/World%20of%20Spectrum%20June%202017%20Mirror/sinclair/
			Local file links starting with /zxdb/sinclair/ refer to content added afterwards.
			These files are currently stored at https://spectrumcomputing.co.uk/zxdb/sinclair/


			https://github.com/zxdb/ZXDB/blob/master/README.md
			*/


			char juego[PATH_MAX];
			util_get_file_no_directory(query_id,juego);
			util_normalize_name(juego);

			char tempdir[PATH_MAX];
			//sprintf (tempdir,"%s/download",get_tmpdir_base() );
            get_online_download_path(tempdir);

			menu_filesel_mkdir(tempdir);
			sprintf (archivo_temp,"%s/%s",tempdir,juego);


			char url_juego_final[PATH_MAX];
			char host_final[PATH_MAX];

			int ssl_use;

			menu_zxinfo_get_final_url(url_juego,host_final,url_juego_final,&ssl_use);

			debug_printf (VERBOSE_DEBUG,"Downloading file from host %s (SSL=%d) url %s",host_final,ssl_use,url_juego_final);

            char ssl_sni_host_name[NETWORK_MAX_URL]="";
            int descargando_spectrumcomputing=0;

                if (!strcmp(host_final,"spectrumcomputing.co.uk")) {
                    descargando_spectrumcomputing=1;
                    //Descargas desde spectrum computing requieren SNI cuando hay activado su proteccion de cloudflare
                    strcpy(ssl_sni_host_name,"spectrumcomputing.co.uk");
                }

			int ret=menu_download_file(host_final,url_juego_final,archivo_temp,ssl_use,1024*1024,ssl_sni_host_name);  //1 MB mas que suficiente

			if (ret==200) {
                //Si descarga de spectrumcomputing
                if (descargando_spectrumcomputing) {
                    menu_first_aid("download_spectrumcomputing");
                }

                strcpy(quickload_file,archivo_temp);

                //Creamos enlace directo en escritorio
                zxvision_create_configurable_icon_file_type(F_FUNCION_DESKTOP_GENERIC_SMARTLOAD,quickload_file);

                //y abrimos menu de smartload
				quickfile=quickload_file;
				menu_smartload(0);

                //printf("Archivo final: %s\n",quickload_file);

				return;
			}
			else {
				if (ret<0) {
					menu_network_error(ret);
				}
				else {
					debug_printf(VERBOSE_ERR,"Error downloading. Return code: %d",ret);
				}

			}
		}
	} while (1);
}


char menu_network_http_request_url[NETWORK_MAX_URL]="";

void menu_network_http_request(MENU_ITEM_PARAMETERS)
{
	int http_code;
	char *mem;
	char *mem_after_headers;
	char host[100];
	//char url[100];
	char s_skip_headers[2];
	char s_add_headers[200];

	host[0]=0;
	//url[0]=0;

	strcpy(s_skip_headers,"0");
	//s_skip_headers[0]='0';
	s_add_headers[0]=0;

	menu_ventana_scanf("host?",host,100);
	menu_ventana_scanf("url?",menu_network_http_request_url,NETWORK_MAX_URL);
	menu_ventana_scanf("add headers",s_add_headers,200);

	int l=strlen(s_add_headers);
	if (l>0) {
		s_add_headers[l++]='\r';
		s_add_headers[l++]='\n';
		s_add_headers[l++]=0;

	}

	menu_ventana_scanf("skip ret headers?(0/1)",s_skip_headers,2);
	int skip_headers=parse_string_to_number(s_skip_headers);
	int total_leidos;
	char redirect_url[NETWORK_MAX_URL];

	int use_ssl=0;
    char ssl_sni_host_name[NETWORK_MAX_URL]="";

#ifdef COMPILE_SSL
	char s_use_ssl[2];
	strcpy(s_use_ssl,"0");
	menu_ventana_scanf("use ssl? (0/1)",s_use_ssl,2);
	use_ssl=parse_string_to_number(s_use_ssl);

    if (use_ssl) {
        menu_ventana_scanf("ssl sni host name?",ssl_sni_host_name,NETWORK_MAX_URL);
    }
#endif



	char *mem_mensaje;

	int retorno=menu_zsock_http(host,menu_network_http_request_url,&http_code,&mem,&total_leidos,&mem_after_headers,skip_headers,s_add_headers,use_ssl,redirect_url,ssl_sni_host_name);
	if (retorno==0 && mem!=NULL) {
		if (skip_headers) {
			if (mem_after_headers) {
				menu_generic_message_format("Http code","%d",http_code);
				mem_mensaje=mem_after_headers;
				//menu_generic_message("Response",mem_after_headers);
			}
		}
		else {
			mem_mensaje=mem;
			//menu_generic_message("Response",mem);
		}
	}

	if (retorno>=0) {

		//Controlar maximo mensaje

		int longitud_mensaje=strlen(mem_mensaje);

		int max_longitud=MAX_TEXTO_GENERIC_MESSAGE-1024;
		//Asumimos el maximo restando 1024, de los posibles altos de linea

		if (longitud_mensaje>max_longitud) {
			//TODO: realmente habria que trocear aqui el mensaje en lineas y ver si el resultado excede el maximo de lineas o el maximo de bytes
			debug_printf (VERBOSE_ERR,"Response too long. Showing only the first %d bytes",max_longitud);
			mem_mensaje[max_longitud]=0;
		}

		menu_generic_message("Response",mem_mensaje);

	}

	else {
		menu_network_error(retorno);
	}



	if (mem!=NULL) free (mem);
}


void menu_online_download_extras(MENU_ITEM_PARAMETERS)
{


	//printf("URL: %s\n",ZESARUX_EXTRAS_URL);



	char host_final[NETWORK_MAX_URL];
	strcpy(host_final,ZESARUX_EXTRAS_HOST);


	char url[NETWORK_MAX_URL];

	strcpy(url,ZESARUX_EXTRAS_URL);


	//200 MB. En versión 9.0 son 100 MB. Mas que suficiente para el futuro
	int estimated_size=200*1024*1024;



	debug_printf(VERBOSE_DEBUG,"Selected url %s",url);

	char archivo_zip[PATH_MAX];

	//Ruta destino en el home
	char dest_dir[PATH_MAX];



//Aunque en Windows no le acaba de gustar, por alguna razón, la ruta al unzip. En Windows lo metemos en la ruta actual
#ifdef MINGW
	dest_dir[0]=0; //Cadena vacia -> carpeta actual
#else
	util_get_home_dir(dest_dir);
#endif




	char zipfilename[PATH_MAX];
	util_get_file_no_directory(url,zipfilename);

	sprintf(archivo_zip,"%s%s",dest_dir,zipfilename);

	int ssl_use=1;


	int ret=menu_download_file(host_final,url,archivo_zip,ssl_use,estimated_size,"");

	if (ret==200) {
		//descomprimimos zip
		char final_mmc_dir[PATH_MAX];
		sprintf(final_mmc_dir,"%s.dir",archivo_zip);

		//Descomprimir con ventana de progreso y pthread aparte de descompresion
		menu_uncompress_zip_progress(archivo_zip,final_mmc_dir);

		if (dest_dir[0]==0) menu_generic_message_format("File downloaded","File has been downloaded and uncompressed to current folder");

		else menu_generic_message_format("File downloaded","File has been downloaded and uncompressed to %s",dest_dir);

		//y abrimos menu de smartload
		strcpy(quickload_file,final_mmc_dir);

		quickfile=quickload_file;
		menu_smartload(0);


		return;
	}
	else {
		if (ret<0) {
			menu_network_error(ret);
		}
		else {
			debug_printf(VERBOSE_ERR,"Error downloading software. Return code: %d",ret);
		}

	}


}



zxvision_window *menu_network_traffic_window;

struct timeval menu_network_traffic_time_antes,menu_network_traffic_time_despues;

unsigned int menu_network_traffic_antes_counter_read=0;
unsigned int menu_network_traffic_antes_counter_write=0;


//Para refrescar 1 por segundo
int contador_network_traffic_overlay_anteriorsegundos=0;

//Para 5 minutos de historial
#define NETWORK_TRAFFIC_MAX_VALUES (60*5)


//Para almacenar los valores ultimos
//En pantalla el mas a la derecha es el mas reciente
//En el array la primera posicion es la mas reciente, por tanto cuando se agrega un valor siempre
//al principio y haciendo scroll del resto

//la primera vez al entrar vaciar el historial
int network_traffic_history_values_inicializado=0;
unsigned int network_traffic_history_values_read[NETWORK_TRAFFIC_MAX_VALUES];
unsigned int network_traffic_history_values_write[NETWORK_TRAFFIC_MAX_VALUES];

//Los que hay en el array realmente, desde 0 hasta NETWORK_TRAFFIC_MAX_VALUES
int network_traffic_history_total_valores_insertados=0;

void menu_network_traffic_scroll(unsigned int *valores)
{
    int i;

    for (i=NETWORK_TRAFFIC_MAX_VALUES-1;i>0;i--) {
        valores[i]=valores[i-1];
    }
}

void menu_network_traffic_insert_read(unsigned int valor)
{
    menu_network_traffic_scroll(network_traffic_history_values_read);
    network_traffic_history_values_read[0]=valor;
}

void menu_network_traffic_insert_write(unsigned int valor)
{
    menu_network_traffic_scroll(network_traffic_history_values_write);
    network_traffic_history_values_write[0]=valor;
}

void menu_network_traffic_draw_graph(zxvision_window *w,unsigned int *lista_valores,int ancho_pixeles_grafica,int alto_pixeles_grafica,
    int offset_x,int offset_y,int *p_maximo_valor,int *p_minimo_valor,int *p_medio_valor,int *p_ultimo_valor)
{
    int x,y;
    int indice=0;
    int i;

    *p_ultimo_valor=lista_valores[0];

    int total_insertados=network_traffic_history_total_valores_insertados;

    if (total_insertados<0) total_insertados=1; //para evitar posibles divisiones por 0

    //Obtener el maximo valor para escalar la grafica segun eso
    unsigned int maximo_valor=1; //partimos de 1 para evitar dividir por 0

    for (i=0;i<total_insertados;i++) {
        unsigned int valor=lista_valores[i];
        if (valor>maximo_valor) maximo_valor=valor;
    }

    *p_maximo_valor=maximo_valor;

    //Obtener el minimo valor. Partimos del maximo
    unsigned int minimo_valor=maximo_valor;

    for (i=0;i<total_insertados;i++) {
        unsigned int valor=lista_valores[i];
        if (valor<minimo_valor) minimo_valor=valor;
    }

    *p_minimo_valor=minimo_valor;


    //Sacar valor medio
    z80_64bit suma=0;

    for (i=0;i<total_insertados;i++) {
        suma +=lista_valores[i];
    }

    suma /=total_insertados;
    *p_medio_valor=suma;

    for (x=ancho_pixeles_grafica;x>=0;x--) {
        //escalar al alto

        unsigned int valor;

        //Si el indice se ha ido de sitio, devolver 0
        if (indice>=NETWORK_TRAFFIC_MAX_VALUES) valor=0;
        else valor=lista_valores[indice++];


        int alto=(valor*alto_pixeles_grafica)/maximo_valor;
        //quitarle 1 pixel porque si no quedan "restos" por arriba
        alto--;

        //Y linea vertical hasta el alto
        //y=alto_pixeles_grafica-1;





        //linea de color. Desde abajo hasta arriba
        for (y=0;alto>=0;alto--,y++) {
            zxvision_putpixel(w,offset_x+x,offset_y+alto_pixeles_grafica-1-y,ESTILO_GUI_COLOR_WAVEFORM);
        }

        //Y resto con fondo
        for (;y<alto_pixeles_grafica;y++) {
            zxvision_putpixel(w,offset_x+x,offset_y+alto_pixeles_grafica-1-y,ESTILO_GUI_PAPEL_NORMAL);
        }

        //Linea abajo del todo para indicar el 0
        zxvision_putpixel(w,offset_x+x,offset_y+alto_pixeles_grafica-1,ESTILO_GUI_COLOR_WAVEFORM);

    }

}

void menu_network_traffic_overlay(void)
{

    menu_speech_set_tecla_pulsada(); //Si no, envia continuamente todo ese texto a speech

    //si ventana minimizada, no ejecutar todo el codigo de overlay
    if (menu_network_traffic_window->is_minimized) return;


    //TODO: agregar gestion dirty, o sea refrescar cuando se haya movido, redimensionado, etc,
    //o quiza ni eso , puede que no haga falta


    //Cada segundo
    int diferencia_tiempo=contador_segundo_infinito-contador_network_traffic_overlay_anteriorsegundos;

    if (diferencia_tiempo>50*20) {
        contador_network_traffic_overlay_anteriorsegundos=contador_segundo_infinito;

        //En microsegundos
        long transcurrido=timer_stats_diference_time(&menu_network_traffic_time_antes,&menu_network_traffic_time_despues);
        //En cuanto pase 1 segundo. TODO esto luego lo hare con contador_segundo como siempre
        if (transcurrido>1000000) {
            int transcurrido_ms=transcurrido/1000;
            timer_stats_current_time(&menu_network_traffic_time_antes);

            unsigned int diferencia_read;
            unsigned int diferencia_write;

            //Si contador ha dado la vuelta
            if (network_traffic_counter_read<menu_network_traffic_antes_counter_read) diferencia_read=0;
            else diferencia_read=network_traffic_counter_read-menu_network_traffic_antes_counter_read;

            if (network_traffic_counter_write<menu_network_traffic_antes_counter_write) diferencia_write=0;
            else diferencia_write=network_traffic_counter_write-menu_network_traffic_antes_counter_write;

            //Sacar justo el trafico en 1 s (1000 ms)

            //cuidado con dividir por 0
            if (transcurrido_ms==0) {
                diferencia_read=0;
                diferencia_write=0;
            }
            else {
                diferencia_read=(diferencia_read*1000)/transcurrido_ms;
                diferencia_write=(diferencia_write*1000)/transcurrido_ms;
            }


            if (network_traffic_history_total_valores_insertados<NETWORK_TRAFFIC_MAX_VALUES) {
                network_traffic_history_total_valores_insertados++;
            }

            menu_network_traffic_insert_read(diferencia_read);
            menu_network_traffic_insert_write(diferencia_write);


            //zxvision_print_string_defaults_fillspc_format(menu_network_traffic_window,1,0,
            //    "Traffic: %8u kbyte/s read - %8u kbyte/s write",diferencia_read/1000,diferencia_write/1000);

            //printf("Traffic: %8u kbyte/s read - %8u kbyte/s write\n",diferencia_read/1000,diferencia_write/1000);

            menu_network_traffic_antes_counter_read=network_traffic_counter_read;
            menu_network_traffic_antes_counter_write=network_traffic_counter_write;

            //int i;

            //temp mostrar

            /*
            printf("Read\n");
            for (i=0;i<10;i++) {
                printf("%u\n",network_traffic_history_values_read[i]);
            }

            printf("Write\n");
            for (i=0;i<10;i++) {
                printf("%u\n",network_traffic_history_values_write[i]);
            }*/



            //esto se tiene que ajustar segun el tamaño de ventana
            int ancho_pixeles_grafica;
            int alto_pixeles_grafica;


            //Ubicacion de las graficas:
            //Margen izquierdo (char width) - Grafica read - Margen central (char width) - Grafica write
            ancho_pixeles_grafica=((menu_network_traffic_window->visible_width)*menu_char_width)/2;
            ancho_pixeles_grafica-=menu_char_width*3;

            alto_pixeles_grafica=(menu_network_traffic_window->visible_height)*menu_char_height;
            alto_pixeles_grafica -=menu_char_height*6;



            //borrar las primera lineas de texto
            zxvision_print_string_defaults_fillspc(menu_network_traffic_window,1,0,"");
            zxvision_print_string_defaults_fillspc(menu_network_traffic_window,1,1,"");
            zxvision_print_string_defaults_fillspc(menu_network_traffic_window,1,2,"");
            zxvision_print_string_defaults_fillspc(menu_network_traffic_window,1,3,"");


            //Si mostrar cabecera texto o no, en caso que sea ventana muy pequeña
            int offset_grafico_y=menu_char_height*4;
            int mostrar_texto=1;

            if (menu_network_traffic_window->visible_height < 7) {

                offset_grafico_y=0;


                alto_pixeles_grafica=(menu_network_traffic_window->visible_height)*menu_char_height;
                //quitarle solo los 2 habituales de alto ventana
                alto_pixeles_grafica -=menu_char_height*2;

                //printf("Sin texto. alto %d offset %d\n",alto_pixeles_grafica,offset_grafico_y);

                mostrar_texto=0;
            }


            if (ancho_pixeles_grafica>NETWORK_TRAFFIC_MAX_VALUES) ancho_pixeles_grafica=NETWORK_TRAFFIC_MAX_VALUES;
            if (ancho_pixeles_grafica<menu_char_width) ancho_pixeles_grafica=menu_char_width;

            if (alto_pixeles_grafica<menu_char_height) alto_pixeles_grafica=menu_char_height;



            int p_maximo_valor_read,p_minimo_valor_read,p_medio_valor_read,p_ultimo_valor_read;
            int p_maximo_valor_write,p_minimo_valor_write,p_medio_valor_write,p_ultimo_valor_write;

            menu_network_traffic_draw_graph(menu_network_traffic_window,network_traffic_history_values_read,
                ancho_pixeles_grafica,alto_pixeles_grafica,menu_char_width,offset_grafico_y,
                &p_maximo_valor_read,&p_minimo_valor_read,&p_medio_valor_read,&p_ultimo_valor_read);


            int pos_x_medio=(menu_network_traffic_window->visible_width)/2;


            menu_network_traffic_draw_graph(menu_network_traffic_window,network_traffic_history_values_write,
                ancho_pixeles_grafica,alto_pixeles_grafica,pos_x_medio*menu_char_width,offset_grafico_y,
                &p_maximo_valor_write,&p_minimo_valor_write,&p_medio_valor_write,&p_ultimo_valor_write);


            if (mostrar_texto) {

                char buffer_average_read[100];
                char buffer_max_read[100];
                char buffer_min_read[100];
                char buffer_last_read[100];

                char buffer_average_write[100];
                char buffer_max_write[100];
                char buffer_min_write[100];
                char buffer_last_write[100];

                get_size_bps_human_friendly(p_medio_valor_read,buffer_average_read);
                get_size_bps_human_friendly(p_maximo_valor_read,buffer_max_read);
                get_size_bps_human_friendly(p_minimo_valor_read,buffer_min_read);
                get_size_bps_human_friendly(p_ultimo_valor_read,buffer_last_read);

                get_size_bps_human_friendly(p_medio_valor_write,buffer_average_write);
                get_size_bps_human_friendly(p_maximo_valor_write,buffer_max_write);
                get_size_bps_human_friendly(p_minimo_valor_write,buffer_min_write);
                get_size_bps_human_friendly(p_ultimo_valor_write,buffer_last_write);



                //La primera linea es la que determina si usamos el formato corto en ancho
                char buffer_primera_linea_read[100];
                sprintf(buffer_primera_linea_read,"Read. Avg %s",buffer_average_read);
                int longitud_linea_read=strlen(buffer_primera_linea_read);

                if (longitud_linea_read>=pos_x_medio ) {
                    zxvision_print_string_defaults_format(menu_network_traffic_window,1,0,
                        "%s",buffer_average_read);
                    zxvision_print_string_defaults_format(menu_network_traffic_window,1,1,
                        "%s",buffer_last_read);
                    zxvision_print_string_defaults_format(menu_network_traffic_window,1,2,
                        "%s",buffer_max_read);
                    zxvision_print_string_defaults_format(menu_network_traffic_window,1,3,
                        "%s",buffer_min_read);

                    zxvision_print_string_defaults_format(menu_network_traffic_window,pos_x_medio,0,
                        "%s",buffer_average_write);
                    zxvision_print_string_defaults_format(menu_network_traffic_window,pos_x_medio,1,
                        "%s",buffer_last_write);
                    zxvision_print_string_defaults_format(menu_network_traffic_window,pos_x_medio,2,
                        "%s",buffer_max_write);
                    zxvision_print_string_defaults_format(menu_network_traffic_window,pos_x_medio,3,
                        "%s",buffer_min_write);
                }

                else {

                    zxvision_print_string_defaults_format(menu_network_traffic_window,1,0,
                        "%s",buffer_primera_linea_read);
                    zxvision_print_string_defaults_format(menu_network_traffic_window,1,1,
                        "Last %s",buffer_last_read);
                    zxvision_print_string_defaults_format(menu_network_traffic_window,1,2,
                        "Max %s",buffer_max_read);
                    zxvision_print_string_defaults_format(menu_network_traffic_window,1,3,
                        "Min %s",buffer_min_read);

                    zxvision_print_string_defaults_format(menu_network_traffic_window,pos_x_medio,0,
                        "Write. Avg %s",buffer_average_write);
                    zxvision_print_string_defaults_format(menu_network_traffic_window,pos_x_medio,1,
                        "Last %s",buffer_last_write);
                    zxvision_print_string_defaults_format(menu_network_traffic_window,pos_x_medio,2,
                        "Max %s",buffer_max_write);
                    zxvision_print_string_defaults_format(menu_network_traffic_window,pos_x_medio,3,
                        "Min %s",buffer_min_write);
                }

            }


        }
    }

    //Refrescar
    zxvision_draw_window_contents(menu_network_traffic_window);

}




//Almacenar la estructura de ventana aqui para que se pueda referenciar desde otros sitios
zxvision_window zxvision_window_network_traffic;


void menu_network_traffic(MENU_ITEM_PARAMETERS)
{
	menu_espera_no_tecla();

    if (!menu_multitarea) {
        menu_warn_message("This window needs multitask enabled");
        return;
    }

    timer_stats_current_time(&menu_network_traffic_time_antes);
    menu_network_traffic_antes_counter_read=network_traffic_counter_read;
    menu_network_traffic_antes_counter_write=network_traffic_counter_write;

    //Vaciar el historial si conviene
    //la primera vez al entrar vaciar el historial
    if (!network_traffic_history_values_inicializado) {
        network_traffic_history_values_inicializado=1;

        int i;
        for (i=0;i<NETWORK_TRAFFIC_MAX_VALUES;i++) {
            network_traffic_history_values_read[i]=0;
            network_traffic_history_values_write[i]=0;
        }
    }

    zxvision_window *ventana;
    ventana=&zxvision_window_network_traffic;

	//IMPORTANTE! no crear ventana si ya existe. Esto hay que hacerlo en todas las ventanas que permiten background.
	//si no se hiciera, se crearia la misma ventana, y en la lista de ventanas activas , al redibujarse,
	//la primera ventana repetida apuntaria a la segunda, que es el mismo puntero, y redibujaria la misma, y se quedaria en bucle colgado
	//zxvision_delete_window_if_exists(ventana);

    //Crear ventana si no existe
    if (!zxvision_if_window_already_exists(ventana)) {
        int xventana,yventana,ancho_ventana,alto_ventana,is_minimized,is_maximized,ancho_antes_minimize,alto_antes_minimize;

        if (!util_find_window_geometry("networktraffic",&xventana,&yventana,&ancho_ventana,&alto_ventana,&is_minimized,&is_maximized,&ancho_antes_minimize,&alto_antes_minimize)) {
            ancho_ventana=54;
            alto_ventana=20;

            xventana=menu_center_x()-ancho_ventana/2;
            yventana=menu_center_y()-alto_ventana/2;
        }


        zxvision_new_window_gn_cim(ventana,xventana,yventana,ancho_ventana,alto_ventana,ancho_ventana-1,alto_ventana-2,"Network Traffic",
            "networktraffic",is_minimized,is_maximized,ancho_antes_minimize,alto_antes_minimize);

        ventana->can_be_backgrounded=1;

    }

    //Si ya existe, activar esta ventana
    else {
        zxvision_activate_this_window(ventana);
    }

	zxvision_draw_window(ventana);

	z80_byte tecla;


	int salir=0;


    menu_network_traffic_window=ventana; //Decimos que el overlay lo hace sobre la ventana que tenemos aqui


    //cambio overlay
    zxvision_set_window_overlay(ventana,menu_network_traffic_overlay);


    //Toda ventana que este listada en zxvision_known_window_names_array debe permitir poder salir desde aqui
    //Se sale despues de haber inicializado overlay y de cualquier otra variable que necesite el overlay
    if (zxvision_currently_restoring_windows_on_start) {
            //printf ("Saliendo de ventana ya que la estamos restaurando en startup\n");
            return;
    }

    do {


		tecla=zxvision_common_getkey_refresh();


        switch (tecla) {

            case 11:
                //arriba
                //blablabla
            break;



            //Salir con ESC
            case 2:
                salir=1;
            break;

            //O tecla background
            case 3:
                salir=1;
            break;
        }


    } while (salir==0);


	util_add_window_geometry_compact(ventana);

	if (tecla==3) {
		zxvision_message_put_window_background();
	}

	else {

		zxvision_destroy_window(ventana);
	}


}







void menu_network(MENU_ITEM_PARAMETERS)
{
        //Dado que es una variable local, siempre podemos usar este nombre array_menu_common
        menu_item *array_menu_common;
        menu_item item_seleccionado;
        int retorno_menu;
        do {


            menu_add_item_menu_inicial_format(&array_menu_common,MENU_OPCION_NORMAL,menu_uartbridge,menu_network_uartbridge_cond,"~~UART Bridge emulation");
            menu_add_item_menu_spanish_catalan(array_menu_common,"Emulacion Puente ~~UART","Emulacio Pont ~~UART");
			menu_add_item_menu_shortcut(array_menu_common,'u');
			menu_add_item_menu_tooltip(array_menu_common,"Bridge from emulated machine uart ports to a local serial uart device");
			menu_add_item_menu_ayuda(array_menu_common,"Bridge from emulated machine uart ports to a local serial uart device\n"
				"It does NOT emulate a full uart device, just links from the emulated machine ports to a physical local device\n"
				"Available for ZX-Uno, TBBlue and ZX Evolution TSConf");
            menu_add_item_menu_tiene_submenu(array_menu_common);


#ifndef NETWORKING_DISABLED



            menu_add_item_menu_separator(array_menu_common);

            menu_add_item_menu_format(array_menu_common,MENU_OPCION_NORMAL,menu_online_browse_zx81,NULL,"~~ZX81 online browser");
            menu_add_item_menu_spanish(array_menu_common,"Navegador online ~~ZX81");
			menu_add_item_menu_shortcut(array_menu_common,'z');
            menu_add_item_menu_se_cerrara(array_menu_common);
            menu_add_item_menu_genera_ventana(array_menu_common);
            menu_add_item_menu_tooltip(array_menu_common,"Connects to the www.zx81.nl site to download ZX81 games. Many thanks to ZXwebmaster for allowing it");
            menu_add_item_menu_ayuda(array_menu_common,"Connects to the www.zx81.nl site to download ZX81 games. Many thanks to ZXwebmaster for allowing it");


//online browser ya solo es accesible con ssl, porque las descargas tanto de spectrum computing como archive.org necesitan SSL

#ifdef COMPILE_SSL
			menu_add_item_menu_format(array_menu_common,MENU_OPCION_NORMAL,menu_online_browse_zxinfowos,NULL,"~~Speccy online browser");
            menu_add_item_menu_spanish(array_menu_common,"Navegador online ~~Speccy");
			menu_add_item_menu_shortcut(array_menu_common,'s');
            menu_add_item_menu_se_cerrara(array_menu_common);
            menu_add_item_menu_genera_ventana(array_menu_common);
			//Versión con SSL usa zxinfo, spectrum computing y mirror archive.org
			menu_add_item_menu_tooltip(array_menu_common,"It uses zxinfo, spectrum computing and archive.org to download the software. Thanks to Thomas Heckmann and Peter Jones for allowing it");
			menu_add_item_menu_ayuda(array_menu_common,  "It uses zxinfo, spectrum computing and archive.org to download the software. Thanks to Thomas Heckmann and Peter Jones for allowing it");


//#else
			//Versión sin SSL usa zxinfo, servidor WOS
			//menu_add_item_menu_tooltip(array_menu_common,"It uses zxinfo and WOS to download the software. Thanks to Thomas Heckmann and Lee Fogarty for allowing it");
			//menu_add_item_menu_ayuda(array_menu_common,  "It uses zxinfo and WOS to download the software. Thanks to Thomas Heckmann and Lee Fogarty for allowing it");



			menu_add_item_menu_en_es_ca(array_menu_common,MENU_OPCION_NORMAL,menu_online_download_extras,NULL,
                "Download ZEsarUX e~~xtras","Descargar e~~xtras ZEsarUX","Descarregar e~~xtres ZEsarUX");
			menu_add_item_menu_shortcut(array_menu_common,'x');
            menu_add_item_menu_se_cerrara(array_menu_common);
            menu_add_item_menu_genera_ventana(array_menu_common);
			menu_add_item_menu_tooltip(array_menu_common,"Download ZEsarUX extras package");
			menu_add_item_menu_ayuda(array_menu_common,"ZEsarUX extras package contains lots of documentation, sample games, demos, etc");

#endif


			menu_add_item_menu_format(array_menu_common,MENU_OPCION_NORMAL,menu_network_traffic,NULL,"Network traffic");
            menu_add_item_menu_se_cerrara(array_menu_common);
            menu_add_item_menu_genera_ventana(array_menu_common);



			menu_add_item_menu_en_es_ca(array_menu_common,MENU_OPCION_NORMAL,menu_network_http_request,NULL,
                "Test Http request","Test peticion Http","Test peticio Http");
            menu_add_item_menu_se_cerrara(array_menu_common);
            menu_add_item_menu_genera_ventana(array_menu_common);
            menu_add_item_menu_es_avanzado(array_menu_common);

            menu_add_item_menu_separator(array_menu_common);

            menu_add_item_menu_en_es_ca(array_menu_common,MENU_OPCION_NORMAL,NULL,NULL,
            "--Network Gaming--","--Juego en Red--","--Joc en Xarxa");


			menu_add_item_menu_format(array_menu_common,MENU_OPCION_NORMAL,menu_zeng,NULL,"Z~~ENG");
			menu_add_item_menu_shortcut(array_menu_common,'e');
			menu_add_item_menu_tooltip(array_menu_common,"Setup ZEsarUX Network Gaming");
			menu_add_item_menu_ayuda(array_menu_common,"ZEsarUX Network Gaming protocol (ZENG) allows you to play to any emulated game, using two or more ZEsarUX instances, "
			  "located each one on any part of the world or in a local network.\n"
			  "Games don't need to be modified, you can use any existing game. "
			  "ZENG works by sending special commands through the ZRCP protocol, so in order to use ZENG you must enable ZRCP protocol on menu settings-debug. "
			  "This protocol listens on tcp port 10000 so you should open your firewall/router to use it. "
			  "One ZEsarUX instance will be the master node and the other instances will be the slaves nodes.\n"
			  "Please do NOT set more than one node as master\n"
			  "When you enable ZENG on all nodes:\n"
			  "-all key/joystick presses will be sent between all nodes\n"
			  "-every two seconds a snapshot will be sent from the master to all the slave nodes\n"
              "-all nodes connect to all other nodes\n"
              "\n"
			  "Note about using joystick: real joystick (and cursors on keyboard) are sent to the other nodes as "
			  "the direction/button (left,right,up,down or fire) but not the type of joystick emulated (kempston, fuller, etc). "
			  "So you must configure same joystick emulation on all nodes. Also, real joystick to keys events are not sent by ZENG, just the joystick event"
			);
            menu_add_item_menu_tiene_submenu(array_menu_common);


			menu_add_item_menu_format(array_menu_common,MENU_OPCION_NORMAL,menu_zeng_online,NULL,"ZENG ~~Online Client");
			menu_add_item_menu_shortcut(array_menu_common,'o');
			menu_add_item_menu_tooltip(array_menu_common,"Connect to a ZEsarUX Network Gaming Online server");
			menu_add_item_menu_ayuda(array_menu_common,"ZEsarUX Network Gaming protocol Online (ZENG Online) allows you to play to any emulated game, using two or more ZEsarUX instances, "
			  "located each one on any part of the world or in a local network.\n"
              "It's similar to ZENG but uses a central online server\n"
			);
            menu_add_item_menu_tiene_submenu(array_menu_common);




//Fin de condicion ifndef NETWORKING_DISABLED
#endif


			menu_add_item_menu_separator(array_menu_common);

            menu_add_ESC_item(array_menu_common);

            menu_add_item_menu_index_full_path(array_menu_common,"Main Menu-> Network","Menú Principal-> Network","Menú Principal-> Network");

            retorno_menu=menu_dibuja_menu(&network_opcion_seleccionada,&item_seleccionado,array_menu_common,"Network Menu","Menú Network","Menú Network" );


                if ((item_seleccionado.tipo_opcion&MENU_OPCION_ESC)==0 && retorno_menu>=0) {
                        //llamamos por valor de funcion
                        if (item_seleccionado.menu_funcion!=NULL) {
                                //printf ("actuamos por funcion\n");
                                item_seleccionado.menu_funcion(item_seleccionado.valor_opcion);

                        }
                }

        } while ( (item_seleccionado.tipo_opcion&MENU_OPCION_ESC)==0 && retorno_menu!=MENU_RETORNO_ESC && !salir_todos_menus);




}








void menu_storage_mmc_reload(MENU_ITEM_PARAMETERS)
{
	if (mmc_read_file_to_memory(valor_opcion)==0) {
		menu_generic_message_splash("Reload MMC","OK. MMC file reloaded");
	}
}

void menu_divmmc_rom_file(MENU_ITEM_PARAMETERS)
{


	//desactivamos diviface divmmc. Así obligamos que el usuario tenga que activarlo de nuevo, recargando del firmware
	divmmc_diviface_disable();


        char *filtros[3];

        filtros[0]="rom";
				filtros[1]="bin";
        filtros[2]=0;


        if (menu_filesel("Select ROM File",filtros, divmmc_rom_name)==1) {
				//Nada

        }
        //Sale con ESC
        else {
                //Quitar nombre
                divmmc_rom_name[0]=0;


        }

				menu_generic_message("Change DIVMMC ROM","OK. Remember to enable DIVMMC paging to load the firmware");
}

void menu_storage_diviface_eprom_write_jumper(MENU_ITEM_PARAMETERS)
{
	diviface_eprom_write_jumper.v ^=1;
}

void menu_storage_mmc_write_protect(MENU_ITEM_PARAMETERS)
{
	mmc_write_protection[valor_opcion].v ^=1;
}


void menu_storage_mmc_persistent_writes(MENU_ITEM_PARAMETERS)
{
	mmc_persistent_writes[valor_opcion].v ^=1;
}

/* No tiene sentido este viewer aqui, mucho mejor el browser
void menu_storage_mmc_viewer(MENU_ITEM_PARAMETERS)
{
	//menu_file_mmc_browser_show(mmc_file_name,"MMC");
	menu_file_viewer_read_file("MMC file viewer",mmc_file_name);
}
*/

void menu_storage_mmc_ide_browser(char *file_image)
{

    //Ver si es tipo plusidedos
    //Con los primeros 10 bytes es suficiente
    z80_byte buffer[10];

    util_load_file_bytes(buffer,file_image,10);

    if (util_if_filesystem_plusidedos(buffer,10)) {
        //printf("Es plusidedos\n");
        menu_file_mmc_browser_show(file_image,"PLUSIDEDOS Image");
    }

    else {
        //printf("Puede que sea fat\n");

	    if (!file_utils_mount_mmc_image(file_image)) {
            menu_debug_file_utils(0);
            file_utils_umount_mmc_image();
        }
    }
}

void menu_storage_mmc_browser(MENU_ITEM_PARAMETERS)
{
	menu_storage_mmc_ide_browser(mmc_file_name[valor_opcion]);
}

void menu_storage_mmc_emulation(MENU_ITEM_PARAMETERS)
{
	if (mmc_enabled[valor_opcion].v) mmc_disable(valor_opcion);
	else mmc_enable(valor_opcion);
}

/*
int menu_storage_mmc_emulation_cond(void)
{
        if (mmc_file_name[0]==0) return 0;
        else return 1;
}
*/

/*
int menu_storage_mmc_if_enabled_cond(void)
{
	return mmc_enabled.v;
}
*/

void menu_storage_zxmmc_emulation(MENU_ITEM_PARAMETERS)
{
	zxmmc_emulation.v ^=1;
}

void menu_storage_divmmc_mmc_ports_emulation(MENU_ITEM_PARAMETERS)
{
        if (divmmc_mmc_ports_enabled.v) divmmc_mmc_ports_disable();
        else divmmc_mmc_ports_enable();
}

void menu_storage_divmmc_diviface(MENU_ITEM_PARAMETERS)
{
	if (divmmc_diviface_enabled.v) divmmc_diviface_disable();
	else {
		divmmc_diviface_enable();
        //Tambien activamos puertos por simplificar la operación al usuario. Luego si quiere el usuario que los desactive
        divmmc_mmc_ports_enable();
	}
}


void menu_storage_mmc_file_after_select_ask_configure_tbblue(void)
{
		if (MACHINE_IS_TBBLUE) {
				if (menu_confirm_yesno("Configure Next MMC settings?")) {

					//Repetir sentencia de usuario:
					//1) Habilitar MMC
					//2) Desactivar DIVMMC paging
					//3) Habilitar divmmc ports
					//4) Hard reset

					//Sabemos que esto estara desactivado pero bueno, mejor lo chequeamos para que no conmute en caso que ya estuviera
					if (mmc_enabled[0].v==0) menu_storage_mmc_emulation(0);

					if (divmmc_diviface_enabled.v) menu_storage_divmmc_diviface(0);

					if (divmmc_mmc_ports_enabled.v==0) menu_storage_divmmc_mmc_ports_emulation(0);

					hard_reset_cpu();

					salir_todos_menus=1;
				}
			}
}

void menu_storage_mmc_file(MENU_ITEM_PARAMETERS)
{

    int tarjeta_seleccionada=valor_opcion;

	mmc_disable(tarjeta_seleccionada);

    mmc_filemap_from_esxdos[tarjeta_seleccionada]=0;

        char *filtros[5];

        filtros[0]="mmc";
		filtros[1]="mmcide";
		filtros[2]="hdf";
		filtros[3]="img";

        filtros[4]=0;


	   //guardamos directorio actual
        char directorio_actual[PATH_MAX];
        getcwd(directorio_actual,PATH_MAX);

              //Obtenemos directorio de trd
        //si no hay directorio, vamos a rutas predefinidas
        if (mmc_file_name[tarjeta_seleccionada][0]==0) menu_chdir_sharedfiles();

        else {
                char directorio[PATH_MAX];
                util_get_dir(mmc_file_name[tarjeta_seleccionada],directorio);
                //printf ("strlen directorio: %d directorio: %s\n",strlen(directorio),directorio);

                //cambiamos a ese directorio, siempre que no sea nulo
                if (directorio[0]!=0) {
                        debug_printf (VERBOSE_INFO,"Changing to last directory: %s",directorio);
                        zvfs_chdir(directorio);
                }
        }



		int ret=menu_filesel("Select MMC File",filtros,mmc_file_name[tarjeta_seleccionada]);
		//volvemos a directorio inicial
        zvfs_chdir(directorio_actual);


        if (ret==1) {
		if (!si_existe_archivo(mmc_file_name[tarjeta_seleccionada])) {
			if (menu_confirm_yesno_texto("File does not exist","Create?")==0) {
                                mmc_file_name[tarjeta_seleccionada][0]=0;
                                return;
                        }

			//Preguntar tamanyo en MB
			char string_tamanyo[5];
			sprintf (string_tamanyo,"32");
			menu_ventana_scanf("Size (in MB)",string_tamanyo,5);
			int size=parse_string_to_number(string_tamanyo);
			if (size<1) {
				debug_printf (VERBOSE_ERR,"Invalid file size");
				mmc_file_name[tarjeta_seleccionada][0]=0;
                                return;
			}

			if (size>=1024) {
				menu_warn_message("Using MMC bigger than 1 GB can be very slow");
			}


			//Crear archivo vacio
		        FILE *ptr_mmcfile;
			ptr_mmcfile=fopen(mmc_file_name[tarjeta_seleccionada],"wb");

		        long long int totalsize=size;
			totalsize=totalsize*1024*1024;
			z80_byte valor_grabar=0;

		        if (ptr_mmcfile!=NULL) {
				while (totalsize) {
					fwrite(&valor_grabar,1,1,ptr_mmcfile);
					totalsize--;
				}
		                fclose(ptr_mmcfile);
		        }

		}

		else {
			//Comprobar aqui tambien el tamanyo
			long long int size=get_file_size(mmc_file_name[tarjeta_seleccionada]);
			if (size>1073741824L) {
				menu_warn_message("Using MMC bigger than 1 GB can be very slow");
            }


			//Y pedir si configurar automaticamente en caso de TBBLUE
            if (tarjeta_seleccionada==0) {
			    menu_storage_mmc_file_after_select_ask_configure_tbblue();
            }

		}


        }
        //Sale con ESC
        else {
                //Quitar nombre
                mmc_file_name[tarjeta_seleccionada][0]=0;


        }
}

void menu_storage_divmmc_diviface_total_ram(MENU_ITEM_PARAMETERS)
{
	diviface_current_ram_memory_bits++;
	if (diviface_current_ram_memory_bits==7) diviface_current_ram_memory_bits=2;
}


//Descargar imagen MMC oficial de tbblue
void menu_storage_mmc_download_tbblue(void)
{
    int tarjeta_seleccionada=0;
	menu_first_aid("tbblue_download_sd_bug");

	//http://zxspectrumnext.online/cspect/tbbluemmc-32mb.zip

	char *host_final="zxnext.uk";


	char url[NETWORK_MAX_URL];

    char image_filename[100];


	int opcion_tamanyo_imagen=menu_simple_three_choices("Image type","Which size?",
														"2 GB",
														"4 GB",
														"8 GB");

	int estimated_size=64*1024*1024;

    //Esos archivos son de 70, 70 y 145 MB respectivamente. por eso el estimated size no lo pongo a 2 GB por ejemplo
    //pero le doy un margen bastante grande

	switch (opcion_tamanyo_imagen) {
		case 1:
			strcpy(url,"/hosted/index_files/hdfimages/cspect-next-2gb.zip");
            strcpy(image_filename,"2gb/cspect-next-2gb.img");
			estimated_size=512*1024*1024;
		break;

		case 2:
			strcpy(url,"/hosted/index_files/hdfimages/cspect-next-4gb.zip");
            strcpy(image_filename,"4gb/cspect-next-4gb.img");
			estimated_size=512*1024*1024;
		break;


		case 3:
			strcpy(url,"/hosted/index_files/hdfimages/cspect-next-8gb.zip");
            strcpy(image_filename,"8gb/cspect-next-8gb.img");
			estimated_size=512*1024*1024;
		break;


		default:
			return;
		break;
	}


	debug_printf(VERBOSE_DEBUG,"Selected url %s",url);

	char archivo_zip[PATH_MAX];

	//Ruta destino en el home
	char dest_dir[PATH_MAX];



//Aunque en Windows no le acaba de gustar, por alguna razón, la ruta al unzip. En Windows lo metemos en la ruta actual
#ifdef MINGW
	dest_dir[0]=0; //Cadena vacia -> carpeta actual
#else
	util_get_home_dir(dest_dir);
#endif




	char zipfilename[PATH_MAX];
	util_get_file_no_directory(url,zipfilename);

	//sprintf(archivo_zip,"%s%s",dest_dir,"tbbluemmc-32mb.zip");
	sprintf(archivo_zip,"%s%s",dest_dir,zipfilename);

	int ssl_use=1;


	int ret=menu_download_file(host_final,url,archivo_zip,ssl_use,estimated_size,"");

	if (ret==200) {
		//descomprimimos zip
		char final_mmc_dir[PATH_MAX];
		sprintf(final_mmc_dir,"%s.dir",archivo_zip);

		//Descomprimir con ventana de progreso y pthread aparte de descompresion
		menu_uncompress_zip_progress(archivo_zip,final_mmc_dir);

		//y abrimos menu de mmc
        //TODO: porque abrimos menu de mmc si ya le estamos pasando la ruta exacta al archivo de imagen?
        //quizá es por evitar errores, por si la gente de Next cambia el nombre de la imagen, que el usuario pueda escogerla
		sprintf(mmc_file_name[tarjeta_seleccionada],"%s/%s",final_mmc_dir,image_filename);

		menu_storage_mmc_file(0);

		return;
	}
	else {
		if (ret<0) {
			menu_network_error(ret);
		}
		else {
			debug_printf(VERBOSE_ERR,"Error downloading software. Return code: %d",ret);
		}

	}






}

void menu_storage_mmc_use_local_tbblue(void)
{
	char buffer_nombre[PATH_MAX];

	if (find_sharedfile("tbblue.mmc",buffer_nombre)) {
		//Asignar la MMC
		//TODO: esto mete ruta relativa (en caso de . o ../Resources). Se podria meter ruta absoluta
		strcpy(mmc_file_name[0],buffer_nombre);


		//Luego preguntar si aplicar divmmc etc
		menu_storage_mmc_file_after_select_ask_configure_tbblue();
	}

	else {
		menu_error_message("tbblue.mmc image not found");
	}

}

void menu_storage_mmc_sdhc_addressing(MENU_ITEM_PARAMETERS)
{
    mmc_sdhc_addressing[valor_opcion].v ^=1;
}


void menu_storage_mmc_autoconfigure_tbblue(MENU_ITEM_PARAMETERS)
{

    //desmapear posible mapeo de esxdos
    mmc_filemap_from_esxdos[0]=0;
    mmc_filemap_from_esxdos[1]=0;

	int tipo_imagen;

    int available_download=1;

//Si no hay phreads ni ssl, solo se puede usar la opcion local
#ifdef NETWORKING_DISABLED
	available_download=0;
#endif

#ifndef COMPILE_SSL
    available_download=0;
#endif

    if (available_download) {
        tipo_imagen=menu_simple_two_choices("SD Image type","Included or download?","Use included in ZEsarUX","Download from official repo");
    }
    else {
        tipo_imagen=1;
    }

	switch(tipo_imagen) {
		case 1:
			//Usar imagen local
			menu_storage_mmc_use_local_tbblue();
		break;

		case 2:
			//Usar repo remoto
			menu_storage_mmc_download_tbblue();
		break;
	}

}



//menu MMC/Divmmc
void menu_mmc_divmmc(MENU_ITEM_PARAMETERS)
{
    menu_item *array_menu_mmc_divmmc;
    menu_item item_seleccionado;
    int retorno_menu;
    do {

        char string_mmc_file_shown[17];
        char string_divmmc_rom_file_shown[10];

        menu_add_item_menu_inicial(&array_menu_mmc_divmmc,"",MENU_OPCION_UNASSIGNED,NULL,NULL);

        int i;

        for (i=0;i<MMC_MAX_CARDS;i++) {

            menu_add_item_menu_en_es_ca(array_menu_mmc_divmmc,MENU_OPCION_NORMAL,NULL,NULL,
            "--Card","--Tarjeta","--Targeta");
            menu_add_item_menu_sufijo_format(array_menu_mmc_divmmc," %d--",i);

            if (!mmc_filemap_from_esxdos[i]) {


                menu_tape_settings_trunc_name(mmc_file_name[i],string_mmc_file_shown,17);
                menu_add_item_menu_en_es_ca(array_menu_mmc_divmmc,MENU_OPCION_NORMAL,menu_storage_mmc_file,NULL,
                    "MMC File","Archivo MMC","Arxiu MMC");
                menu_add_item_menu_sufijo_format(array_menu_mmc_divmmc," [%s]",string_mmc_file_shown);
                menu_add_item_menu_prefijo(array_menu_mmc_divmmc,"    ");
                menu_add_item_menu_valor_opcion(array_menu_mmc_divmmc,i);
                //menu_add_item_menu_shortcut(array_menu_mmc_divmmc,'m');
                menu_add_item_menu_tooltip(array_menu_mmc_divmmc,"MMC Emulation file");
                menu_add_item_menu_ayuda(array_menu_mmc_divmmc,"MMC Emulation file");

            }
            else {
                menu_add_item_menu_en_es_ca(array_menu_mmc_divmmc,MENU_OPCION_NORMAL,NULL,NULL,
                    "MMC is mapped from ESXDOS","MMC está mapeado desde ESXDOS","MMC está mapejat desde ESXDOS");
                menu_add_item_menu_prefijo(array_menu_mmc_divmmc,"    ");

                menu_add_item_menu_en_es_ca(array_menu_mmc_divmmc,MENU_OPCION_NORMAL,menu_storage_mmc_file,NULL,
                    "~~MMC File","Archivo ~~MMC","Arxiu ~~MMC");
                menu_add_item_menu_prefijo(array_menu_mmc_divmmc,"    ");
                menu_add_item_menu_valor_opcion(array_menu_mmc_divmmc,i);
            }

            if (MACHINE_IS_TBBLUE && i==0) {

                menu_add_item_menu_en_es_ca(array_menu_mmc_divmmc,MENU_OPCION_NORMAL,menu_storage_mmc_autoconfigure_tbblue,NULL,
                    "Autoconfigure Next SD","Autoconfigurar Next SD","Autoconfigurar Next SD");
                menu_add_item_menu_prefijo(array_menu_mmc_divmmc,"    ");

            }

            if (mmc_file_name[i][0]) {
                menu_add_item_menu_en_es_ca(array_menu_mmc_divmmc,MENU_OPCION_NORMAL,menu_storage_mmc_emulation,NULL,
                    "MMC Emulation","Emulación MMC","Emulació MMC");
                menu_add_item_menu_prefijo_format(array_menu_mmc_divmmc,"[%c] ", (mmc_enabled[i].v ? 'X' : ' '));
                menu_add_item_menu_valor_opcion(array_menu_mmc_divmmc,i);
                //menu_add_item_menu_shortcut(array_menu_mmc_divmmc,'e');
                menu_add_item_menu_tooltip(array_menu_mmc_divmmc,"MMC Emulation");
                menu_add_item_menu_ayuda(array_menu_mmc_divmmc,"MMC Emulation");
            }

            menu_add_item_menu_en_es_ca(array_menu_mmc_divmmc,MENU_OPCION_NORMAL,menu_storage_mmc_sdhc_addressing,NULL,
                "Enable SDHC addressing","Activar direccionamiento SDHC","Activar direccionament SDHC");
            menu_add_item_menu_prefijo_format(array_menu_mmc_divmmc,"[%c] ", (mmc_sdhc_addressing[i].v ? 'X' : ' '));
            menu_add_item_menu_valor_opcion(array_menu_mmc_divmmc,i);
            menu_add_item_menu_tooltip(array_menu_mmc_divmmc,"Enable SDHC addressing (block addressing instead of byte addressing)");
            menu_add_item_menu_ayuda(array_menu_mmc_divmmc,"Enable SDHC addressing (block addressing instead of byte addressing)");



            menu_add_item_menu_en_es_ca(array_menu_mmc_divmmc,MENU_OPCION_NORMAL,menu_storage_mmc_write_protect,NULL,
                "Write protect","Protección escritura","Protecció escriptura");
            menu_add_item_menu_prefijo_format(array_menu_mmc_divmmc,"[%c] ", (mmc_write_protection[i].v ? 'X' : ' '));
            menu_add_item_menu_valor_opcion(array_menu_mmc_divmmc,i);
            //menu_add_item_menu_shortcut(array_menu_mmc_divmmc,'i');
            menu_add_item_menu_tooltip(array_menu_mmc_divmmc,"If MMC disk is write protected");
            menu_add_item_menu_ayuda(array_menu_mmc_divmmc,"If MMC disk is write protected");


            menu_add_item_menu_en_es_ca(array_menu_mmc_divmmc,MENU_OPCION_NORMAL,menu_storage_mmc_persistent_writes,NULL,
                "Persistent Writes","Escrituras Persistentes","Escriptures Persistents");
            menu_add_item_menu_prefijo_format(array_menu_mmc_divmmc,"[%c] ",(mmc_persistent_writes[i].v ? 'X' : ' ') );
            menu_add_item_menu_valor_opcion(array_menu_mmc_divmmc,i);
            menu_add_item_menu_tooltip(array_menu_mmc_divmmc,"Tells if MMC writes are saved to disk");
            menu_add_item_menu_ayuda(array_menu_mmc_divmmc,"Tells if MMC writes are saved to disk. "
            "Note: all writing operations to MMC are always saved to internal memory (unless you disable write permission), but this setting "
            "tells if these changes are written to disk or not."
            );

            if (mmc_enabled[i].v) {
                menu_add_item_menu_en_es_ca(array_menu_mmc_divmmc,MENU_OPCION_NORMAL,menu_storage_mmc_reload,NULL,
                    "Reload MMC file","Recargar archivo MMC","Recarregar arxiu MMC");
                menu_add_item_menu_prefijo(array_menu_mmc_divmmc,"    ");
                menu_add_item_menu_valor_opcion(array_menu_mmc_divmmc,i);
                menu_add_item_menu_tooltip(array_menu_mmc_divmmc,"Reload MMC contents from MMC file to emulator memory");
                menu_add_item_menu_ayuda(array_menu_mmc_divmmc,"Reload MMC contents from MMC file to emulator memory. You can modify the MMC file "
                    "outside the emulator, and reload its contents without having to disable and enable MM.");
            }



            if (mmc_file_name[i][0]) {
                menu_add_item_menu_en_es_ca(array_menu_mmc_divmmc,MENU_OPCION_NORMAL,menu_storage_mmc_browser,NULL,
                    "MMC Browser Card","MMC Browser Tarjeta","MMC Browser Targeta");
                menu_add_item_menu_prefijo(array_menu_mmc_divmmc,"    ");
                menu_add_item_menu_valor_opcion(array_menu_mmc_divmmc,i);
                //menu_add_item_menu_shortcut(array_menu_mmc_divmmc,'b');
                menu_add_item_menu_tooltip(array_menu_mmc_divmmc,"MMC Browser");
                menu_add_item_menu_ayuda(array_menu_mmc_divmmc,"MMC Browser");
                menu_add_item_menu_genera_ventana(array_menu_mmc_divmmc);
                menu_add_item_menu_se_cerrara(array_menu_mmc_divmmc);

            }


            menu_add_item_menu_separator(array_menu_mmc_divmmc);

        }




        menu_add_item_menu(array_menu_mmc_divmmc,"",MENU_OPCION_SEPARADOR,NULL,NULL);

        menu_add_item_menu_en_es_ca(array_menu_mmc_divmmc,MENU_OPCION_NORMAL,menu_storage_divmmc_diviface,NULL,
            "~~DIVMMC paging","Paginación ~~DIVMMC","Paginació ~~DIVMMC");
        menu_add_item_menu_prefijo_format(array_menu_mmc_divmmc,"[%c] ",(divmmc_diviface_enabled.v ? 'X' : ' ') );
        menu_add_item_menu_shortcut(array_menu_mmc_divmmc,'d');
        menu_add_item_menu_tooltip(array_menu_mmc_divmmc,"Enables DIVMMC paging and firmware, and DIVMMC access ports if MMC emulation is enabled");
        menu_add_item_menu_ayuda(array_menu_mmc_divmmc,"Enables DIVMMC paging and firmware, and DIVMMC access ports if MMC emulation is enabled");

        if (divmmc_diviface_enabled.v) {
            menu_add_item_menu_en_es_ca(array_menu_mmc_divmmc,MENU_OPCION_NORMAL,menu_storage_divmmc_diviface_total_ram,NULL,
                "DIVMMC RAM","DIVMMC RAM","DIVMMC RAM");
            menu_add_item_menu_sufijo_format(array_menu_mmc_divmmc," [%d KB]",get_diviface_total_ram() );
            menu_add_item_menu_prefijo(array_menu_mmc_divmmc,"    ");
            menu_add_item_menu_tooltip(array_menu_mmc_divmmc,"Changes DIVMMC RAM");
            menu_add_item_menu_ayuda(array_menu_mmc_divmmc,"Changes DIVMMC RAM");
            menu_add_item_menu_es_avanzado(array_menu_mmc_divmmc);


        }

        //En tbblue y zxuno no tiene sentido mostrar estas opciones, no las usa
        //incluso la anterior de divmmc ram, las dos maquinas tienen 128kb de divmmc ram por defecto,
        //esta opcion si que la leen esas máquinas aunque alterar ese valor puede tener efectos indeseados
        if (!MACHINE_IS_ZXUNO && !MACHINE_IS_TBBLUE) {

            if (divmmc_rom_name[0]==0) sprintf (string_divmmc_rom_file_shown,"Default");
            else menu_tape_settings_trunc_name(divmmc_rom_name, string_divmmc_rom_file_shown,10);
            menu_add_item_menu_en_es_ca(array_menu_mmc_divmmc,MENU_OPCION_NORMAL,menu_divmmc_rom_file,NULL,
                "DIVMMC EPROM File","Archivo EPROM DIVMMC","Arxiu EPROM DIVMMC");
            menu_add_item_menu_sufijo_format(array_menu_mmc_divmmc," [%s]", string_divmmc_rom_file_shown);
            menu_add_item_menu_prefijo(array_menu_mmc_divmmc,"    ");
            menu_add_item_menu_es_avanzado(array_menu_mmc_divmmc);

            menu_add_item_menu_tooltip(array_menu_mmc_divmmc,"Changes DIVMMC firmware eprom file");
            menu_add_item_menu_ayuda(array_menu_mmc_divmmc,"Changes DIVMMC firmware eprom file");


            if (divmmc_diviface_enabled.v) {
                menu_add_item_menu_en_es_ca(array_menu_mmc_divmmc,MENU_OPCION_NORMAL,menu_storage_diviface_eprom_write_jumper,NULL,
                    "Firmware writeable","Firmware escribible","Firmware escribible");
                menu_add_item_menu_prefijo_format(array_menu_mmc_divmmc,"[%c] ",(diviface_eprom_write_jumper.v ? 'X' : ' ') );
                menu_add_item_menu_tooltip(array_menu_mmc_divmmc,"Allows writing to DivIDE/DivMMC eprom");
                menu_add_item_menu_ayuda(array_menu_mmc_divmmc,"Allows writing to DivIDE/DivMMC eprom. Changes are lost when you exit the emulator");
                menu_add_item_menu_es_avanzado(array_menu_mmc_divmmc);
            }

        }


        //if (mmc_enabled[0].v || mmc_enabled[1].v) {
            menu_add_item_menu_en_es_ca(array_menu_mmc_divmmc,MENU_OPCION_NORMAL,menu_storage_divmmc_mmc_ports_emulation,NULL,
                "DIVMMC ~~ports","~~Puertos DIVMMC","~~Ports DIVMMC");
            menu_add_item_menu_prefijo_format(array_menu_mmc_divmmc,"[%c] ",(divmmc_mmc_ports_enabled.v ? 'X' : ' ') );
            menu_add_item_menu_shortcut(array_menu_mmc_divmmc,'p');
            menu_add_item_menu_tooltip(array_menu_mmc_divmmc,"Enables DIVMMC access ports");
            menu_add_item_menu_ayuda(array_menu_mmc_divmmc,"Enables DIVMMC access ports. Requires enabling MMC Emulation");


            menu_add_item_menu(array_menu_mmc_divmmc,"",MENU_OPCION_SEPARADOR,NULL,NULL);


            menu_add_item_menu_en_es_ca(array_menu_mmc_divmmc,MENU_OPCION_NORMAL,menu_storage_zxmmc_emulation,NULL,
                "~~ZXMMC Enabled","~~ZXMMC Activado","~~ZXMMC Activat");
            menu_add_item_menu_prefijo_format(array_menu_mmc_divmmc,"[%c] ",(zxmmc_emulation.v ? 'X' : ' ') );
            menu_add_item_menu_shortcut(array_menu_mmc_divmmc,'z');
            menu_add_item_menu_tooltip(array_menu_mmc_divmmc,"Access MMC using ZXMMC");
            menu_add_item_menu_ayuda(array_menu_mmc_divmmc,"Enables ZXMMC ports to access MMC");
        //}


        menu_add_item_menu_separator(array_menu_mmc_divmmc);

        /* No tiene sentido este viewer aqui, mucho mejor el browser
        menu_add_item_menu_format(array_menu_mmc_divmmc,MENU_OPCION_NORMAL,menu_storage_mmc_viewer,menu_storage_mmc_emulation_cond,"MMC ~~Viewer");
        menu_add_item_menu_shortcut(array_menu_mmc_divmmc,'v');
        menu_add_item_menu_tooltip(array_menu_mmc_divmmc,"MMC Viewer");
        menu_add_item_menu_ayuda(array_menu_mmc_divmmc,"MMC Viewer");
        */





        menu_add_item_menu(array_menu_mmc_divmmc,"",MENU_OPCION_SEPARADOR,NULL,NULL);
        //menu_add_item_menu(array_menu_mmc_divmmc,"ESC Back",MENU_OPCION_NORMAL|MENU_OPCION_ESC,NULL,NULL);
        menu_add_ESC_item(array_menu_mmc_divmmc);

        retorno_menu=menu_dibuja_menu_no_title_lang(&mmc_divmmc_opcion_seleccionada,&item_seleccionado,array_menu_mmc_divmmc,"SD/MMC" );


        if ((item_seleccionado.tipo_opcion&MENU_OPCION_ESC)==0 && retorno_menu>=0) {
                //llamamos por valor de funcion
                if (item_seleccionado.menu_funcion!=NULL) {
                        //printf ("actuamos por funcion\n");
                        item_seleccionado.menu_funcion(item_seleccionado.valor_opcion);

                }
        }

    } while ( (item_seleccionado.tipo_opcion&MENU_OPCION_ESC)==0 && retorno_menu!=MENU_RETORNO_ESC && !salir_todos_menus);




}





void menu_storage_ide_emulation(MENU_ITEM_PARAMETERS)
{
        if (ide_enabled.v) ide_disable();
        else ide_enable();
}


int menu_storage_ide_emulation_cond(void)
{
        if (ide_file_name[0]==0) return 0;
        else return 1;
}

/*
void menu_storage_divide_emulation(MENU_ITEM_PARAMETERS)
{
        if (divide_enabled.v) divide_disable();
        else divide_enable();
}
*/


void menu_storage_divide_ide_ports_emulation(MENU_ITEM_PARAMETERS)
{
        if (divide_ide_ports_enabled.v) divide_ide_ports_disable();
        else divide_ide_ports_enable();
}

void menu_storage_divide_diviface(MENU_ITEM_PARAMETERS)
{
        if (divide_diviface_enabled.v) divide_diviface_disable();
        else {
            divide_diviface_enable();
            //Tambien activamos puertos. Luego si quiere el usuario que los desactive
		    divide_ide_ports_enable();
        }
}



void menu_storage_ide_file(MENU_ITEM_PARAMETERS)
{

        ide_disable();

        char *filtros[3];

        filtros[0]="ide";
		filtros[1]="mmcide";
        filtros[2]=0;


        if (menu_filesel("Select IDE File",filtros,ide_file_name)==1) {
                if (!si_existe_archivo(ide_file_name)) {
                        if (menu_confirm_yesno_texto("File does not exist","Create?")==0) {
                                ide_file_name[0]=0;
                                return;
                        }

                        //Preguntar tamanyo en MB
                        char string_tamanyo[5];
                        sprintf (string_tamanyo,"32");
                        menu_ventana_scanf("Size (in MB)",string_tamanyo,5);
                        int size=parse_string_to_number(string_tamanyo);
                        if (size<1) {
                                debug_printf (VERBOSE_ERR,"Invalid file size");
                                ide_file_name[0]=0;
                                return;
                        }

                        if (size>=1024) {
                                menu_warn_message("Using IDE bigger than 1 GB can be very slow");
                        }


                        //Crear archivo vacio
                        FILE *ptr_idefile;
                        ptr_idefile=fopen(ide_file_name,"wb");

   long long int totalsize=size;
                        totalsize=totalsize*1024*1024;
                        z80_byte valor_grabar=0;

                        if (ptr_idefile!=NULL) {
                                while (totalsize) {
                                        fwrite(&valor_grabar,1,1,ptr_idefile);
                                        totalsize--;
                                }
                                fclose(ptr_idefile);
                        }

                }

                else {
                        //Comprobar aqui tambien el tamanyo
                        long long int size=get_file_size(ide_file_name);
                        if (size>1073741824L) {
                                menu_warn_message("Using IDE bigger than 1 GB can be very slow");
                        }
                }


        }
        //Sale con ESC
        else {
                //Quitar nombre
                ide_file_name[0]=0;


        }
}


int menu_storage_ide_if_enabled_cond(void)
{
	return ide_enabled.v;
}

void menu_eightbitsimple_enable(MENU_ITEM_PARAMETERS)
{
	if (eight_bit_simple_ide_enabled.v) eight_bit_simple_ide_disable();
	else eight_bit_simple_ide_enable();
}

void menu_atomlite_enable(MENU_ITEM_PARAMETERS)
{
        int reset=0;

        if (atomlite_enabled.v) {

                reset=menu_confirm_yesno_texto("Confirm reset","Load normal rom and reset?");

                atomlite_enabled.v=0;
        }

        else {
                reset=menu_confirm_yesno_texto("Confirm reset","Load atomlite rom and reset?");
                atomlite_enabled.v=1;
        }

        if (reset) {
                set_machine(NULL);
                cold_start_cpu_registers();
                reset_cpu();
		salir_todos_menus=1;
        }

}

void menu_storage_ide_reload(MENU_ITEM_PARAMETERS)
{
	if (ide_read_file_to_memory()==0) {
		menu_generic_message_splash("Reload IDE","OK. IDE file reloaded");
	}
}


void menu_divide_rom_file(MENU_ITEM_PARAMETERS)
{


	//desactivamos diviface divide. Así obligamos que el usuario tenga que activarlo de nuevo, recargando del firmware
	divide_diviface_disable();


        char *filtros[3];

        filtros[0]="rom";
				filtros[1]="bin";
        filtros[2]=0;


        if (menu_filesel("Select ROM File",filtros, divide_rom_name)==1) {
				//Nada

        }
        //Sale con ESC
        else {
                //Quitar nombre
                divide_rom_name[0]=0;


        }

				menu_generic_message("Change DIVIDE ROM","OK. Remember to enable DIVIDE paging to load the firmware");
}

void menu_storage_ide_write_protect(MENU_ITEM_PARAMETERS)
{
	ide_write_protection.v ^=1;
}

void menu_storage_ide_persistent_writes(MENU_ITEM_PARAMETERS)
{
	ide_persistent_writes.v ^=1;
}


/* No tiene sentido este viewer aqui, mucho mejor el browser
void menu_storage_ide_viewer(MENU_ITEM_PARAMETERS)
{
	//menu_file_mmc_browser_show(ide_file_name,"IDE");
	menu_file_viewer_read_file("IDE file viewer",ide_file_name);
}
*/

void menu_storage_ide_browser(MENU_ITEM_PARAMETERS)
{
    menu_storage_mmc_ide_browser(ide_file_name);
}

//menu IDE/Divide
void menu_ide_divide(MENU_ITEM_PARAMETERS)
{
    menu_item *array_menu_ide_divide;
    menu_item item_seleccionado;
    int retorno_menu;
    do {

        char string_ide_file_shown[17];
        char string_divide_rom_file_shown[10];


        menu_tape_settings_trunc_name(ide_file_name,string_ide_file_shown,17);
        menu_add_item_menu_en_es_ca_inicial(&array_menu_ide_divide,MENU_OPCION_NORMAL,menu_storage_ide_file,NULL,
            "~~IDE File","Archivo ~~IDE","Arxiu ~~IDE");
        menu_add_item_menu_sufijo_format(array_menu_ide_divide," [%s]",string_ide_file_shown);
        menu_add_item_menu_prefijo(array_menu_ide_divide,"    ");
        menu_add_item_menu_shortcut(array_menu_ide_divide,'i');
        menu_add_item_menu_tooltip(array_menu_ide_divide,"IDE Emulation file");
        menu_add_item_menu_ayuda(array_menu_ide_divide,"IDE Emulation file");


        menu_add_item_menu_en_es_ca(array_menu_ide_divide,MENU_OPCION_NORMAL,menu_storage_ide_emulation,menu_storage_ide_emulation_cond,
            "IDE ~~Emulation","~~Emulación IDE","~~Emulació IDE");
        menu_add_item_menu_prefijo_format(array_menu_ide_divide,"[%c] ", (ide_enabled.v ? 'X' : ' '));
        menu_add_item_menu_shortcut(array_menu_ide_divide,'e');
        menu_add_item_menu_tooltip(array_menu_ide_divide,"IDE Emulation");
        menu_add_item_menu_ayuda(array_menu_ide_divide,"IDE Emulation");


        menu_add_item_menu_en_es_ca(array_menu_ide_divide,MENU_OPCION_NORMAL,menu_storage_ide_write_protect,NULL,
            "Wr~~ite protect","Protección Escr~~itura","Protecció Escr~~iptura");
        menu_add_item_menu_prefijo_format(array_menu_ide_divide,"[%c] ", (ide_write_protection.v ? 'X' : ' '));
        menu_add_item_menu_shortcut(array_menu_ide_divide,'i');
        menu_add_item_menu_tooltip(array_menu_ide_divide,"If IDE disk is write protected");
        menu_add_item_menu_ayuda(array_menu_ide_divide,"If IDE disk is write protected");


        menu_add_item_menu_en_es_ca(array_menu_ide_divide,MENU_OPCION_NORMAL,menu_storage_ide_persistent_writes,NULL,
            "Persistent Writes","Escrituras Persistentes","Escriptures Persistents");
        menu_add_item_menu_prefijo_format(array_menu_ide_divide,"[%c] ",(ide_persistent_writes.v ? 'X' : ' ') );
        menu_add_item_menu_tooltip(array_menu_ide_divide,"Tells if IDE writes are saved to disk");
        menu_add_item_menu_ayuda(array_menu_ide_divide,"Tells if IDE writes are saved to disk. "
        "Note: all writing operations to IDE are always saved to internal memory (unless you disable write permission), but this setting "
        "tells if these changes are written to disk or not."
        );




        if (ide_enabled.v) {
            menu_add_item_menu_en_es_ca(array_menu_ide_divide,MENU_OPCION_NORMAL,menu_storage_ide_reload,NULL,
                "Reload IDE file","Recargar archivo IDE","Recarregar arxiu IDE");
            menu_add_item_menu_prefijo(array_menu_ide_divide,"    ");
            menu_add_item_menu_tooltip(array_menu_ide_divide,"Reload IDE contents from IDE file to emulator memory");
            menu_add_item_menu_ayuda(array_menu_ide_divide,"Reload IDE contents from IDE file to emulator memory. You can modify the IDE file "
                                    "outside the emulator, and reload its contents without having to disable and enable IDE");
        }




        if (MACHINE_IS_SPECTRUM) {

            menu_add_item_menu(array_menu_ide_divide,"",MENU_OPCION_SEPARADOR,NULL,NULL);

            menu_add_item_menu_en_es_ca(array_menu_ide_divide,MENU_OPCION_NORMAL,menu_storage_divide_diviface,NULL,
                "~~DIVIDE paging","Paginación ~~DIVIDE","Paginació ~~DIVIDE");
            menu_add_item_menu_prefijo_format(array_menu_ide_divide,"[%c] ",(divide_diviface_enabled.v ? 'X' : ' ') );
            menu_add_item_menu_shortcut(array_menu_ide_divide,'d');
            menu_add_item_menu_tooltip(array_menu_ide_divide,"Enables DIVIDE paging and firmware, and DIVIDE access ports if IDE emulation is enabled");
            menu_add_item_menu_ayuda(array_menu_ide_divide,"Enables DIVIDE paging and firmware, and DIVIDE access ports if IDE emulation is enabled");

            if (divide_diviface_enabled.v) {
                menu_add_item_menu_en_es_ca(array_menu_ide_divide,MENU_OPCION_NORMAL,menu_storage_divmmc_diviface_total_ram,NULL,
                    "DIVIDE RAM","DIVIDE RAM","DIVIDE RAM");
                menu_add_item_menu_sufijo_format(array_menu_ide_divide," [%d KB]",get_diviface_total_ram() );
                menu_add_item_menu_tooltip(array_menu_ide_divide,"Changes DIVIDE RAM");
                menu_add_item_menu_ayuda(array_menu_ide_divide,"Changes DIVIDE RAM");
                menu_add_item_menu_es_avanzado(array_menu_ide_divide);
            }

            if (divide_rom_name[0]==0) sprintf (string_divide_rom_file_shown,"Default");
            else menu_tape_settings_trunc_name(divide_rom_name, string_divide_rom_file_shown,10);
            menu_add_item_menu_en_es_ca(array_menu_ide_divide,MENU_OPCION_NORMAL,menu_divide_rom_file,NULL,
                "DIVIDE EPROM File","Archivo EPROM DIVIDE","Arxiu EPROM DIVIDE");
            menu_add_item_menu_sufijo_format(array_menu_ide_divide," [%s]", string_divide_rom_file_shown);
            menu_add_item_menu_prefijo(array_menu_ide_divide,"    ");
            menu_add_item_menu_es_avanzado(array_menu_ide_divide);

            menu_add_item_menu_tooltip(array_menu_ide_divide,"Changes DIVIDE firmware eprom file");
            menu_add_item_menu_ayuda(array_menu_ide_divide,"Changes DIVIDE firmware eprom file");

            if (divide_diviface_enabled.v) {
                menu_add_item_menu_en_es_ca(array_menu_ide_divide,MENU_OPCION_NORMAL,menu_storage_diviface_eprom_write_jumper,NULL,
                    "Firmware writeable","Firmware escribible","Firmware escribible");
                menu_add_item_menu_prefijo_format(array_menu_ide_divide,"[%c] ",(diviface_eprom_write_jumper.v ? 'X' : ' ') );
                menu_add_item_menu_tooltip(array_menu_ide_divide,"Allows writing to DivIDE/DivMMC eprom");
                menu_add_item_menu_ayuda(array_menu_ide_divide,"Allows writing to DivIDE/DivMMC eprom. Changes are lost when you exit the emulator");
                menu_add_item_menu_es_avanzado(array_menu_ide_divide);
            }



            menu_add_item_menu_en_es_ca(array_menu_ide_divide,MENU_OPCION_NORMAL,menu_storage_divide_ide_ports_emulation,NULL,
                "DIVIDE ~~ports","~~Puertos DIVIDE","~~Ports DIVIDE");
            menu_add_item_menu_prefijo_format(array_menu_ide_divide,"[%c] ",(divide_ide_ports_enabled.v ? 'X' : ' ') );
            menu_add_item_menu_shortcut(array_menu_ide_divide,'p');
            menu_add_item_menu_tooltip(array_menu_ide_divide,"Enables DIVIDE access ports");
            menu_add_item_menu_ayuda(array_menu_ide_divide,"Enables DIVIDE access ports. Requires enabling IDE Emulation");


            menu_add_item_menu(array_menu_ide_divide,"",MENU_OPCION_SEPARADOR,NULL,NULL);

            menu_add_item_menu_en_es_ca(array_menu_ide_divide,MENU_OPCION_NORMAL,menu_eightbitsimple_enable,menu_storage_ide_if_enabled_cond,
                "8-bit simple IDE","8-bit simple IDE","8-bit simple IDE");
            menu_add_item_menu_prefijo_format(array_menu_ide_divide,"[%c] ",(eight_bit_simple_ide_enabled.v ? 'X' : ' ') );
        }


        if (MACHINE_IS_SAM) {
            menu_add_item_menu(array_menu_ide_divide,"",MENU_OPCION_SEPARADOR,NULL,NULL);

            menu_add_item_menu_en_es_ca(array_menu_ide_divide,MENU_OPCION_NORMAL,menu_atomlite_enable,NULL,
                "~~Atom Lite","~~Atom Lite","~~Atom Lite");
            menu_add_item_menu_prefijo_format(array_menu_ide_divide,"[%c] ",(atomlite_enabled.v ? 'X' : ' ' ) );
            menu_add_item_menu_shortcut(array_menu_ide_divide,'a');
            menu_add_item_menu_tooltip(array_menu_ide_divide,"Enable Atom Lite");
            menu_add_item_menu_ayuda(array_menu_ide_divide,"Enable Atom Lite");
        }



        menu_add_item_menu_separator(array_menu_ide_divide);


        /* No tiene sentido este viewer aqui, mucho mejor el browser
        menu_add_item_menu_format(array_menu_ide_divide,MENU_OPCION_NORMAL,menu_storage_ide_viewer,menu_storage_ide_emulation_cond,"IDE ~~Viewer");
        menu_add_item_menu_shortcut(array_menu_ide_divide,'v');
        menu_add_item_menu_tooltip(array_menu_ide_divide,"IDE Viewer");
        menu_add_item_menu_ayuda(array_menu_ide_divide,"IDE Viewer");
        */


        menu_add_item_menu_en_es_ca(array_menu_ide_divide,MENU_OPCION_NORMAL,menu_storage_ide_browser,menu_storage_ide_emulation_cond,
            "IDE ~~Browser","IDE ~~Browser","IDE ~~Browser");
        menu_add_item_menu_prefijo(array_menu_ide_divide,"    ");
        menu_add_item_menu_shortcut(array_menu_ide_divide,'b');
        menu_add_item_menu_tooltip(array_menu_ide_divide,"IDE Browser");
        menu_add_item_menu_ayuda(array_menu_ide_divide,"IDE Browser");
        menu_add_item_menu_genera_ventana(array_menu_ide_divide);
        menu_add_item_menu_se_cerrara(array_menu_ide_divide);


        menu_add_item_menu(array_menu_ide_divide,"",MENU_OPCION_SEPARADOR,NULL,NULL);
        //menu_add_item_menu(array_menu_ide_divide,"ESC Back",MENU_OPCION_NORMAL|MENU_OPCION_ESC,NULL,NULL);
        menu_add_ESC_item(array_menu_ide_divide);

        retorno_menu=menu_dibuja_menu_no_title_lang(&ide_divide_opcion_seleccionada,&item_seleccionado,array_menu_ide_divide,"IDE" );


        if ((item_seleccionado.tipo_opcion&MENU_OPCION_ESC)==0 && retorno_menu>=0) {
            //llamamos por valor de funcion
            if (item_seleccionado.menu_funcion!=NULL) {
                //printf ("actuamos por funcion\n");
                item_seleccionado.menu_funcion(item_seleccionado.valor_opcion);

            }
        }

    } while ( (item_seleccionado.tipo_opcion&MENU_OPCION_ESC)==0 && retorno_menu!=MENU_RETORNO_ESC && !salir_todos_menus);




}

void menu_view_screen(MENU_ITEM_PARAMETERS)
{
    menu_espera_no_tecla();

	//para que no se vea oscuro
	menu_set_menu_abierto(0);
	//modificado_border.v=1;

	menu_cls_refresh_emulated_screen();

	//menu_refresca_pantalla();
    menu_espera_tecla();
	menu_set_menu_abierto(1);
	menu_espera_no_tecla();
	modificado_border.v=1;
}

void menu_display_load_screen(MENU_ITEM_PARAMETERS)
{

    char screen_load_file[PATH_MAX];

    char *filtros[2];

    filtros[0]="scr";
    filtros[1]=0;


    if (menu_filesel("Select Screen File",filtros,screen_load_file)==1) {
    load_screen(screen_load_file);
            //Y salimos de todos los menus
            salir_todos_menus=1;

    }

}

void menu_display_save_screen(MENU_ITEM_PARAMETERS)
{

	char screen_save_file[PATH_MAX];

	char *filtros[6];


	if (MACHINE_IS_SPECTRUM) {
		filtros[0]="scr";
		filtros[1]="pbm";
		filtros[2]="bmp";
        filtros[3]="txt";
        filtros[4]="stl";
		filtros[5]=0;
	}

	else if (MACHINE_IS_ZX8081) {
        filtros[0]="scr";
		filtros[1]="bmp";
        filtros[2]="txt";
		filtros[3]=0;
	}

	else if (MACHINE_IS_QL) {
        filtros[0]="scr";
		filtros[1]=0;
	}

	else {
		filtros[0]="bmp";
        filtros[1]="txt";
		filtros[2]=0;
	}


	if (menu_filesel_save("Select Screen File",filtros,screen_save_file)==1) {

		//Ver si archivo existe y preguntar
		struct stat buf_stat;

		if (stat(screen_save_file, &buf_stat)==0) {

			if (menu_confirm_yesno_texto("File exists","Overwrite?")==0) return;

		}

		save_screen(screen_save_file);

        //Si ha ido bien la grabacion
		if (!if_pending_error_message) menu_generic_message_splash("Save Screen","OK. Screen saved");

		//Y salimos de todos los menus
		salir_todos_menus=1;

	}

}

//Realiza quicksave de pantalla y retorna nombre en char nombre, siempre que no sea NULL
void screen_quick_save(char *nombre)
{
  char final_name[PATH_MAX];

    char extension[4];
    if (MACHINE_IS_SPECTRUM) strcpy(extension,"scr");
    else strcpy(extension,"bmp");

  char time_string[40];

  snapshot_get_date_time_string(time_string);

  if (snapshot_autosave_interval_quicksave_directory[0]==0) sprintf (final_name,"%s-%s.%s",snapshot_autosave_interval_quicksave_name,time_string,extension);

  else sprintf (final_name,"%s/%s-%s.%s",snapshot_autosave_interval_quicksave_directory,snapshot_autosave_interval_quicksave_name,time_string,extension);

  save_screen(final_name);

  if (nombre!=NULL) strcpy(nombre,final_name);
}


void menu_display_quicksave_scr(MENU_ITEM_PARAMETERS)
{
	char final_name[PATH_MAX];


	screen_quick_save(final_name);

    //Crear un icono siempre que zx desktop este visible
    //Y en este caso solo para spectrum
    //pues es un scr y tiene sentido gestionarlo desde escritorio
    if (MACHINE_IS_SPECTRUM) {
        zxvision_create_configurable_icon_file_type(F_FUNCION_DESKTOP_GENERIC_SMARTLOAD,final_name);
    }


	menu_generic_message_format("Quicksave Screen","OK. Screen file name: %s",final_name);

}


void menu_unpaws_ungac(MENU_ITEM_PARAMETERS)
{

	char mensaje[1024];

	int retorno=util_unpawsetc_dump_words(mensaje);

	if (retorno>=0) {
		menu_generic_message_format("Extract Words",mensaje);
	}

	else {
		debug_printf (VERBOSE_ERR,mensaje);
	}


}

void menu_display_window_list_info(zxvision_window *w)
{

    //calcular tamaño ocupado por la ventana (lo que ocupan los caracteres)
    int memory_used=zxvision_get_size_text_buffer(w->total_width,w->total_height);

    menu_generic_message_format("Window information",
        "PID: %u\n"
        "Title: %s\n"
        "Name: %s\n"
        "Position: %d,%d\n"
        "Visible size: %dX%d\n"
        "Total size: %dX%d (%d KiB)\n"
        "Scroll %d,%d\n"
        "Minimized: %s\n"
        "Maximized: %s\n"
        "Always visible: %s\n"
        "Immutable: %s\n"
        "Can be resized: %s\n"
        "Can be minimized: %s\n"
        ,
        w->pid,w->window_title,w->geometry_name,w->x,w->y,w->visible_width,w->visible_height,w->total_width,w->total_height,memory_used/1024,
        w->offset_x,w->offset_y,
        (w->is_minimized ? "Yes" : "No"),
        (w->is_maximized ? "Yes" : "No"),
        (w->always_visible ? "Yes" : "No"),
        (w->not_altered_by_massive_changes ? "Yes" : "No"),
        (w->can_be_resized ? "Yes" : "No"),
        (w->can_be_minimized ? "Yes" : "No")
    );


}

void menu_display_window_list_create_window(zxvision_window *ventana)
{

    //Crear ventana si no existe
    if (!zxvision_if_window_already_exists(ventana)) {

        int x,y,ancho,alto,is_minimized,is_maximized,ancho_antes_minimize,alto_antes_minimize;

        if (!util_find_window_geometry("processmanagement",&x,&y,&ancho,&alto,&is_minimized,&is_maximized,&ancho_antes_minimize,&alto_antes_minimize)) {
            ancho=55;
            alto=20;

            x=menu_center_x()-ancho/2;
            y=menu_center_y()-alto/2;
        }


        zxvision_new_window_gn_cim(ventana,x,y,ancho,alto,ancho-1,alto-2,"Process Management","processmanagement",is_minimized,is_maximized,ancho_antes_minimize,alto_antes_minimize);

        ventana->can_be_backgrounded=1;

    }

    //Si ya existe, activar esta ventana
    else {

        zxvision_activate_this_window(ventana);
    }

    zxvision_draw_window(ventana);
}

int menu_display_window_conmutar_ventana=0;

zxvision_window zxvision_window_menu_window_list;

//nombre de la ventana (geometry name) seleccionda
char menu_display_window_list_selected_window[MAX_NAME_WINDOW_GEOMETRY]="";

void menu_display_window_list_item(MENU_ITEM_PARAMETERS)
{
	zxvision_window *ventana;

	//ventana=zxvision_return_n_window_from_top(valor_opcion);

    ventana=zxvision_find_window_in_background(menu_display_window_list_selected_window);

	if (ventana==NULL) {
		menu_error_message("Can not find that window");
		return;
	}

    int selected_ourself=0;

    if (!strcmp(menu_display_window_list_selected_window,"processmanagement")) {
        //Se ha seleccionado la propia ventana, hay acciones que no se pueden hacer
        selected_ourself=1;
    }

    //printf("Ventana pulsada: %s\n",ventana->window_title);


    int tipo=menu_simple_ten_choices("Action","Do you want to","Switch to","Move to top","Move to bottom",
        (ventana->is_minimized ? "Unminimize" : "Minimize"),
        (ventana->is_maximized ? "Unmaximize" : "Maximize"),
        "Switch always visible","Information","Close","Create link on ZX Desktop","Copy contents");

    if (tipo==0) return; //ESC


    zxvision_window *window_management_ventana;
    window_management_ventana=&zxvision_window_menu_window_list;


    switch (tipo) {

	    case 1:
            if (!selected_ourself) {


                //TODO: esto funciona aunque no estoy del todo seguro que vaya a ir bien siempre...
                clicked_on_background_windows=1;
                which_window_clicked_on_background=ventana;

                //Decir que hay que salir de aqui yendo a background, pero sin tener que dejar flag de background para la siguiente ventana
                menu_display_window_conmutar_ventana=1;

            }

        break;

        case 2:
            //Este move on top deja la ventana actual la que le indiquemos, entonces,
            //hay que borrar la propia de process management para abrirla luego, de tal manera que al abrirla
            //si situa justo por encima de la que queremos enviar al top
            zxvision_destroy_window(window_management_ventana);
            zxvision_window_move_this_window_on_top(ventana);
            menu_display_window_list_create_window(window_management_ventana);
        break;

        case 3:
            if (!selected_ourself) {
                zxvision_window_move_this_window_to_bottom(ventana);
            }
        break;

        case 4:
            zxvision_toggle_minimize_window(ventana);
        break;

        case 5:
            zxvision_toggle_maximize_window(ventana);
        break;

        case 6:
            ventana->always_visible ^=1;
        break;

        case 7:
            menu_display_window_list_info(ventana);
        break;


        case 8:
            if (selected_ourself) {
                debug_printf(VERBOSE_ERR,"Can not close ourself");
            }
            else {
	            zxvision_window_delete_this_window(ventana);
            }
        break;

        case 9:
            zxvision_create_link_desktop_from_window(ventana);
        break;


        case 10:
            zxvision_copy_contents_to_clipboard(ventana);
            menu_generic_message_splash("Clipboard","Text copied to ZEsarUX clipboard. Go to file utils and press P to paste to a file");
        break;

    }
}

void menu_display_window_close_all(MENU_ITEM_PARAMETERS)
{
	if (menu_confirm_yesno("Close all windows")) {
		zxvision_window_delete_all_windows();
		cls_menu_overlay();
        menu_generic_message_splash("Close all windows","OK. All windows closed");
	}
}


void menu_display_window_list_get_window_flags(zxvision_window *ventana,char *texto)
{

    //Si es una ventana que no permite background, poner como flags "WIN" o algo asi, para indicar que no es un proceso
    if (!ventana->can_be_backgrounded) {
        strcpy(texto,"WIN");
        return;
    }

    if (!ventana->is_maximized && !ventana->is_minimized && !ventana->always_visible) {
        texto[0]=0;
        return;
    }

    sprintf(texto," [%s%s%s]",
        (ventana->is_maximized   ? "M" : " "),
        (ventana->is_minimized   ? "m" : " "),
        (ventana->always_visible ? "V" : " ")
    );
}

#define MAX_WINDOW_FLAGS_LENGHT 32

//Para que quepa toda la info de ventana, flags y tiempo usado
#define MAX_ITEM_WINDOW_NAME (ZXVISION_MAX_WINDOW_TITLE+MAX_WINDOW_FLAGS_LENGHT+100)

int menu_display_window_list_espacios_nombre_ventana=23;
int menu_display_window_list_espacios_flags=6;

long menu_display_get_total_time(void)
{
    //Tiempo renderizando menus (overlays, normal_text) y tiempo emulacion
    return core_render_menu_overlay_difftime+core_cpu_timer_frame_difftime+sensor_get_value("last_emul_render");;
}

void menu_display_window_list_get_item_window(char *texto_destino,zxvision_window *item_ventana_puntero)
{
    //Si esta minimizada o Maximizada
    char window_flags[MAX_WINDOW_FLAGS_LENGHT];


    menu_display_window_list_get_window_flags(item_ventana_puntero,window_flags);

    int porcentaje;

    if (!menu_display_get_total_time()) porcentaje=0;

    else porcentaje=((item_ventana_puntero->last_spent_time_overlay)*100)/menu_display_get_total_time();

    //TODO: de momento 4 digitos para el pid
    //Al final vendria % ) pero no lo metemos aqui, pues las llamadas a menu_display_window_list_get_item_window
    //desde los items de menus lo hacen pasar a funcion de formateo (tipo printf) y se cargaria el %
    sprintf(texto_destino,
        "%4u %-*s%*s %7ld us (%3d",item_ventana_puntero->pid,menu_display_window_list_espacios_nombre_ventana,item_ventana_puntero->window_title,
        menu_display_window_list_espacios_flags,window_flags,
        item_ventana_puntero->last_spent_time_overlay,porcentaje);
}

zxvision_window *menu_display_window_list_window;

int menu_display_window_list_valor_contador_segundo_anterior;

//nos excluimos cuando es la ventana activa y no el background
//int menu_display_window_list_excluirnos_nosotros=0;



void menu_display_window_list_print_item(zxvision_window *w,int linea,char *window_text)
{



    if (linea==menu_display_window_list_opcion_seleccionada && zxvision_window_is_active_drawn(w) ) {
        //linea donde esta el cursor y es la ventana activa. invertir

        zxvision_print_string_fillspc(w,1,linea,
            ESTILO_GUI_TINTA_SELECCIONADO,ESTILO_GUI_PAPEL_SELECCIONADO,0,window_text);

    }
    else {

        zxvision_print_string_defaults_fillspc(w,1,linea,
            window_text);
    }
}

//Indicar que el overlay no tiene que actualizar, usado al pulsar en un proceso,
//en que se abre nueva ventana y entonces el cursor estaria apuntando al proceso que no hemos pulsado, y seria confuso para el usuario
int menu_display_window_list_overlay_do_not_update=0;

void menu_display_window_list_overlay(void)
{



    //de tal manera que solo llame a normal_overlay de texto cuando no estan en background

    //printf("Inicio efectivo overlay window list\n");

    //TODO: tiempo total transcurrido en esta ventana cuando esta activa, habria que considerar los return posibles
    //y actualizar last_spent_time_overlay de manera correspondiente


    menu_speech_set_tecla_pulsada(); //Si no, envia continuamente todo ese texto a speech

    //si ventana minimizada, no ejecutar todo el codigo de overlay
    if (menu_display_window_list_window->is_minimized) return;


    //esto hara ejecutar esto 5 veces por segundo
    if ( (((contador_segundo%200) == 0 && menu_display_window_list_valor_contador_segundo_anterior!=contador_segundo) ||
        menu_multitarea==0) && menu_display_window_list_overlay_do_not_update==0) {
        menu_display_window_list_valor_contador_segundo_anterior=contador_segundo;

        //printf("refrescando. contador segundo: %d\n",contador_segundo);

        zxvision_cls(menu_display_window_list_window);


        menu_display_window_list_print_item(menu_display_window_list_window,0,"PID  Process name            Flags  Time spent");
        menu_display_window_list_print_item(menu_display_window_list_window,1,"---Top---");


		zxvision_window *item_ventana_puntero=zxvision_current_window;


        int linea=2;

        //Si esta minimizada o Maximizada
        //char window_flags[MAX_WINDOW_FLAGS_LENGHT];

		while (item_ventana_puntero!=NULL) {

            //excluirnos nosotros mismos

            int mostrar=1;



            /*if (menu_display_window_list_excluirnos_nosotros) {
                if (item_ventana_puntero==menu_display_window_list_window) mostrar=0;
            }*/

            if (mostrar) {


                char window_text[MAX_ITEM_WINDOW_NAME];
                char pre_window_text[MAX_ITEM_WINDOW_NAME];


                menu_display_window_list_get_item_window(pre_window_text,item_ventana_puntero);

			    sprintf(window_text,"%s %%)",pre_window_text);




                menu_display_window_list_print_item(menu_display_window_list_window,linea,window_text);



                linea++;

                //printf("Dibujar %s\n",item_ventana_puntero->window_title);


            }

            else {
                //printf("NO Dibujar %s\n",item_ventana_puntero->window_title);
            }

			item_ventana_puntero=item_ventana_puntero->previous_window;
		}

        menu_display_window_list_print_item(menu_display_window_list_window,linea,"---Bottom---");

        char buffer_additional_items[MAX_TEXTO_OPCION];


        int porcentaje;

        if (!menu_display_get_total_time()) porcentaje=0;
        else porcentaje=(normal_overlay_time_total_drawing_overlay*100)/menu_display_get_total_time();

        sprintf(buffer_additional_items,"     ZX Vision text render   [SYS] %7ld us (%3d %%)",normal_overlay_time_total_drawing_overlay,porcentaje);
        menu_display_window_list_print_item(menu_display_window_list_window,linea+1,buffer_additional_items);


        if (!menu_display_get_total_time()) porcentaje=0;
        else porcentaje=(core_cpu_timer_frame_difftime*100)/menu_display_get_total_time();

        sprintf(buffer_additional_items,"     Emulation frame         [SYS] %7ld us (%3d %%)",core_cpu_timer_frame_difftime,porcentaje);
        menu_display_window_list_print_item(menu_display_window_list_window,linea+2,buffer_additional_items);


        long emulated_display_render=sensor_get_value("last_emul_render");
        if (!menu_display_get_total_time()) porcentaje=0;
        else porcentaje=(emulated_display_render*100)/menu_display_get_total_time();

        sprintf(buffer_additional_items,"     Render emulated display [SYS] %7ld us (%3d %%)",emulated_display_render,porcentaje);
        menu_display_window_list_print_item(menu_display_window_list_window,linea+3,buffer_additional_items);


        //TODO: este tiempo total no es perfecto,
        //deberia coincidir con la suma de todos los procesos
        //hay algún otro tiempo que se me escapa y no se está considerando
        sprintf(buffer_additional_items,"Total time:                        %7ld us",menu_display_get_total_time());
        menu_display_window_list_print_item(menu_display_window_list_window,linea+4,buffer_additional_items);

    }


    zxvision_draw_window_contents(menu_display_window_list_window);

    //printf("FIN overlay window list\n");

}





void menu_display_window_list(MENU_ITEM_PARAMETERS)
{

    zxvision_window *ventana;
    ventana=&zxvision_window_menu_window_list;

    //IMPORTANTE! no crear ventana si ya existe. Esto hay que hacerlo en todas las ventanas que permiten background.
    //si no se hiciera, se crearia la misma ventana, y en la lista de ventanas activas , al redibujarse,
    //la primera ventana repetida apuntaria a la segunda, que es el mismo puntero, y redibujaria la misma, y se quedaria en bucle colgado
    //zxvision_delete_window_if_exists(ventana);

    menu_display_window_list_create_window(ventana);

    menu_display_window_list_window=ventana; //Decimos que el overlay lo hace sobre la ventana que tenemos aqui


    //cambio overlay
    zxvision_set_window_overlay(ventana,menu_display_window_list_overlay);



    //Toda ventana que este listada en zxvision_known_window_names_array debe permitir poder salir desde aqui
    //Se sale despues de haber inicializado overlay y de cualquier otra variable que necesite el overlay
    if (zxvision_currently_restoring_windows_on_start) {
            //printf ("Saliendo de ventana ya que la estamos restaurando en startup\n");
            return;
    }


    //menu_display_window_list_excluirnos_nosotros=1;

    //Dado que es una variable local, siempre podemos usar este nombre array_menu_common
    menu_item *array_menu_common;
    menu_item item_seleccionado;
    int retorno_menu;


    //opcion a 0 siempre al iniciar
    menu_display_window_list_opcion_seleccionada=0;

	do {

		menu_add_item_menu_inicial_format(&array_menu_common,MENU_OPCION_NORMAL,NULL,NULL,"PID  Process name            Flags  Time spent");
        menu_add_item_menu_tabulado(array_menu_common,1,0);

        menu_add_item_menu_format(array_menu_common,MENU_OPCION_NORMAL,NULL,NULL,"---Top---");
        menu_add_item_menu_tabulado(array_menu_common,1,1);

		zxvision_window *item_ventana_puntero=zxvision_current_window;


        int linea=2;


		while (item_ventana_puntero!=NULL) {

            //excluirnos nosotros mismos
            //if (item_ventana_puntero!=ventana) {

                char window_text[MAX_ITEM_WINDOW_NAME];

                menu_display_window_list_get_item_window(window_text,item_ventana_puntero);


			    menu_add_item_menu_format(array_menu_common,MENU_OPCION_NORMAL,menu_display_window_list_item,NULL,
                    "%s %%)",window_text);

                //Agregarle el nombre de geometria para saber que ventana es
                menu_add_item_menu_misc(array_menu_common,item_ventana_puntero->geometry_name);

                menu_add_item_menu_tabulado(array_menu_common,1,linea++);

            //}


			item_ventana_puntero=item_ventana_puntero->previous_window;

		}

		menu_add_item_menu_format(array_menu_common,MENU_OPCION_NORMAL,NULL,NULL,"---Bottom---");
        menu_add_item_menu_tabulado(array_menu_common,1,linea++);



		retorno_menu=menu_dibuja_menu_no_title_lang(&menu_display_window_list_opcion_seleccionada,&item_seleccionado,array_menu_common,"Process management");


        if ((item_seleccionado.tipo_opcion&MENU_OPCION_ESC)==0 && retorno_menu>=0) {
                //llamamos por valor de funcion
                if (item_seleccionado.menu_funcion!=NULL) {

                        menu_display_window_list_overlay_do_not_update=1;

                        //Indicar el nombre de la ventana
                        strcpy(menu_display_window_list_selected_window,item_seleccionado.texto_misc);

                        item_seleccionado.menu_funcion(item_seleccionado.valor_opcion);

                        menu_display_window_list_overlay_do_not_update=0;

                }
        }

    } while ( (item_seleccionado.tipo_opcion&MENU_OPCION_ESC)==0 && retorno_menu!=MENU_RETORNO_ESC &&
        !salir_todos_menus && retorno_menu!=MENU_RETORNO_BACKGROUND && !menu_display_window_conmutar_ventana);



    //menu_display_window_list_excluirnos_nosotros=0;

    //Grabar geometria ventana
    util_add_window_geometry_compact(ventana);


    if (retorno_menu==MENU_RETORNO_BACKGROUND || menu_display_window_conmutar_ventana) {
        zxvision_message_put_window_background();
    }

    else {
        //En caso de menus tabulados, es responsabilidad de este de liberar ventana
        zxvision_destroy_window(ventana);
    }

    menu_display_window_conmutar_ventana=0;

}

//Minimizar todas las ventanas
void menu_display_window_minimize_all_common(void)
{
	//Podemos empezar desde la de arriba por ejemplo, da igual
	zxvision_window *ventana;

	ventana=zxvision_current_window;


	while (ventana!=NULL) {

        if (ventana->not_altered_by_massive_changes==0) {

		//Primero decimos que no esta minimizada
        ventana->is_minimized=0;

        //Luego simulamos accion de pulsar boton de minimizar ventana
        zxvision_handle_click_minimize(ventana);


		//Y guardar la geometria
		util_add_window_geometry_compact(ventana);

        }

		ventana=ventana->previous_window;
	}

	cls_menu_overlay();


}

//Maximizar todas las ventanas
void menu_display_window_maximize_all_common(void)
{
	//Podemos empezar desde la de arriba por ejemplo, da igual
	zxvision_window *ventana;

	ventana=zxvision_current_window;


	while (ventana!=NULL) {

        if (ventana->not_altered_by_massive_changes==0) {
		//Primero decimos que no esta maximizada
        ventana->is_maximized=0;

        //Luego simulamos accion de pulsar boton de maximizar ventana
        zxvision_handle_maximize(ventana);


		//Y guardar la geometria
		util_add_window_geometry_compact(ventana);

        }

		ventana=ventana->previous_window;
	}

	cls_menu_overlay();


}

//Reducir todas las ventanas a un tamaño pequeño "razonable" de 20x10, si es que no eran ya asi de pequeñas
void menu_display_window_reduce_all_common(void)
{
	//Podemos empezar desde la de arriba por ejemplo, da igual
	zxvision_window *ventana;

	ventana=zxvision_current_window;

	int ancho_maximo=20;
	int alto_maximo=10;


	while (ventana!=NULL) {


        if (ventana->not_altered_by_massive_changes==0) {
		//La hacemos mas pequeña si es que es mas grande
		//if (ventana->visible_width>ancho_maximo) ventana->visible_width=ancho_maximo;
		//if (ventana->visible_height>alto_maximo) ventana->visible_height=alto_maximo;

        //decir que no esta minimizada
        ventana->is_minimized=0;

		//Siempre la hacemos al tamaño indicado
		//ventana->visible_width=ancho_maximo;
		//ventana->visible_height=alto_maximo;
        //Usar siempre estas dos funciones para cambiar tamaño visible
        zxvision_set_visible_width(ventana,ancho_maximo);
        zxvision_set_visible_height(ventana,alto_maximo);

		//Y guardar la geometria
		util_add_window_geometry_compact(ventana);

        }

		ventana=ventana->previous_window;
	}

	cls_menu_overlay();
}


void menu_display_window_rearrange_all_common(void)
{
	zxvision_rearrange_background_windows(0,0);
}

void menu_display_window_rearrange_all(MENU_ITEM_PARAMETERS)
{
    menu_display_window_rearrange_all_common();

    menu_generic_message_splash("Rearrange all","OK. All windows rearranged");
}

void menu_display_window_cascade_all(MENU_ITEM_PARAMETERS)
{
    zxvision_rearrange_background_windows(1,0);

    menu_generic_message_splash("Cascade all","OK. All windows cascaded");
}

void menu_display_window_reduce_all(MENU_ITEM_PARAMETERS)
{
    menu_display_window_reduce_all_common();

    menu_generic_message_splash("Reduce all","OK. All windows reduced");
}

void menu_display_window_reduce_all_rearrange(MENU_ITEM_PARAMETERS)
{
    menu_display_window_reduce_all_common();
    menu_display_window_rearrange_all_common();

    menu_generic_message_splash("Reduce+rearrange all","OK. All windows reduced+rearranged");
}

void menu_display_window_minimize_all(MENU_ITEM_PARAMETERS)
{
    menu_display_window_minimize_all_common();

    menu_generic_message_splash("Minimize all","OK. All windows minimized");
}

void menu_display_window_minimize_all_rearrange(MENU_ITEM_PARAMETERS)
{
    menu_display_window_minimize_all_common();
    menu_display_window_rearrange_all_common();

    menu_generic_message_splash("Minimize+rearrange all","OK. All windows minimized+rearranged");
}

void menu_display_window_maximize_all(MENU_ITEM_PARAMETERS)
{
    menu_display_window_maximize_all_common();

    menu_generic_message_splash("Maximize all","OK. All windows maximized");
}

void menu_zxdesktop_trash_recover(MENU_ITEM_PARAMETERS)
{
    if (menu_confirm_yesno("Recover icon")) {
        zxvision_recover_configurable_icon_from_trash(valor_opcion);
    }
}

void menu_zxdesktop_trash_empty(MENU_ITEM_PARAMETERS)
{
    if (menu_confirm_yesno("Empty trash")) {
        zxvision_empty_trash();
    }
}

//Papelera
void menu_zxdesktop_trash(MENU_ITEM_PARAMETERS)
{
    menu_item *array_menu_common;
    menu_item item_seleccionado;
    int retorno_menu;
    do {

        menu_add_item_menu_inicial(&array_menu_common,"",MENU_OPCION_UNASSIGNED,NULL,NULL);


        char buffer_texto[MAX_LENGTH_TEXT_ICON];

        int i;

        int total_items_trash=0;
        for (i=0;i<MAX_ZXDESKTOP_CONFIGURABLE_ICONS;i++) {


            if (zxdesktop_configurable_icons_list[i].status==ZXDESKTOP_CUSTOM_ICON_DELETED) {

                //int indice_funcion=zxdesktop_configurable_icons_list[i].indice_funcion;
                //sprintf (buffer_texto,"Icon %2d [%s]",i,defined_direct_functions_array[indice_funcion].texto_funcion);
                strcpy (buffer_texto,zxdesktop_configurable_icons_list[i].text_icon);


                //if (i==0) menu_add_item_menu_inicial_format(&array_menu_common,MENU_OPCION_NORMAL,NULL,NULL,buffer_texto);
                menu_add_item_menu_format(array_menu_common,MENU_OPCION_NORMAL,menu_zxdesktop_trash_recover,NULL,buffer_texto);

                menu_add_item_menu_valor_opcion(array_menu_common,i);

                total_items_trash++;

            }


        }

        if (total_items_trash==0) {
            menu_add_item_menu_format(array_menu_common,MENU_OPCION_NORMAL,NULL,NULL,"<Trash is Empty>");
        }
        else {
            menu_add_item_menu_separator(array_menu_common);

            menu_add_item_menu_en_es_ca(array_menu_common,MENU_OPCION_NORMAL,menu_zxdesktop_trash_empty,NULL,
                "Empty Trash","Vaciar Papelera","Buidar Paperera");

        }

        menu_add_item_menu_separator(array_menu_common);

        menu_add_ESC_item(array_menu_common);

        retorno_menu=menu_dibuja_menu_dialogo_no_title_lang(&zxdesktop_trash_opcion_seleccionada,&item_seleccionado,array_menu_common,"Trash Can" );





        if ((item_seleccionado.tipo_opcion&MENU_OPCION_ESC)==0 && retorno_menu>=0) {
                //llamamos por valor de funcion
                if (item_seleccionado.menu_funcion!=NULL) {
                        //printf ("actuamos por funcion\n");
                        item_seleccionado.menu_funcion(item_seleccionado.valor_opcion);

                }
        }

    } while ( (item_seleccionado.tipo_opcion&MENU_OPCION_ESC)==0 && retorno_menu!=MENU_RETORNO_ESC && !salir_todos_menus);
}




void menu_windows(MENU_ITEM_PARAMETERS)
{

	if (!menu_allow_background_windows) {
		menu_warn_message("Background windows setting is not enabled. You can enable it on Settings-> ZX Vision-> Background windows");
		return;
	}

    //Dado que es una variable local, siempre podemos usar este nombre array_menu_common
    menu_item *array_menu_common;
    menu_item item_seleccionado;
    int retorno_menu;
    do {


        menu_add_item_menu_inicial_format(&array_menu_common,MENU_OPCION_NORMAL,menu_display_window_list,NULL,"Process management");
        menu_add_item_menu_spanish_catalan(array_menu_common,"Gestión procesos","Gestió processos");
        menu_add_item_menu_se_cerrara(array_menu_common);
        menu_add_item_menu_genera_ventana(array_menu_common);
        menu_add_item_menu_tooltip(array_menu_common,"Get information about processes and manage them");
        menu_add_item_menu_ayuda(array_menu_common,"Get information about processes and manage them");

        menu_add_item_menu_en_es_ca(array_menu_common,MENU_OPCION_NORMAL,menu_process_switcher,NULL,
            "~~Process switcher","~~Process switcher","~~Process switcher");
        menu_add_item_menu_se_cerrara(array_menu_common);
        menu_add_item_menu_genera_ventana(array_menu_common);
        menu_add_item_menu_tooltip(array_menu_common,"Process switcher");
        menu_add_item_menu_ayuda(array_menu_common,"Process switcher");
        menu_add_item_menu_shortcut(array_menu_common,'p');

        if (zxdesktop_configurable_icons_enabled_and_visible()) {
            menu_add_item_menu_en_es_ca(array_menu_common,MENU_OPCION_NORMAL,menu_zxdesktop_trash,NULL,
                "Trash Can","Papelera","Paperera");
            menu_add_item_menu_se_cerrara(array_menu_common);
            menu_add_item_menu_genera_ventana(array_menu_common);
        }

        menu_add_item_menu_separator(array_menu_common);

        menu_add_item_menu_en_es_ca(array_menu_common,MENU_OPCION_NORMAL,menu_display_window_reduce_all,NULL,
            "~~Reduce all windows","~~Reducir todas las ventanas","~~Reduir totes les finestres");
        menu_add_item_menu_tooltip(array_menu_common,"Reduce windows to maximum size 20x10");
        menu_add_item_menu_ayuda(array_menu_common,"Reduce windows to maximum size 20x10");
        menu_add_item_menu_shortcut(array_menu_common,'r');
        menu_add_item_menu_se_cerrara(array_menu_common);

        menu_add_item_menu_en_es_ca(array_menu_common,MENU_OPCION_NORMAL,menu_display_window_minimize_all,NULL,
            "M~~inimize all windows","M~~inimizar todas las ventanas","M~~inimitzar totes les finestres");
        menu_add_item_menu_shortcut(array_menu_common,'i');
        menu_add_item_menu_se_cerrara(array_menu_common);

        menu_add_item_menu_en_es_ca(array_menu_common,MENU_OPCION_NORMAL,menu_display_window_rearrange_all,NULL,
            "Rearrange all windows","Reubicar todas ventanas","Reubicar totes les finestres");
        menu_add_item_menu_tooltip(array_menu_common,"Rearrange all windows on the ZX Desktop");
        menu_add_item_menu_ayuda(array_menu_common,"Rearrange all windows on the ZX Desktop");
        menu_add_item_menu_se_cerrara(array_menu_common);

        menu_add_item_menu_en_es_ca(array_menu_common,MENU_OPCION_NORMAL,menu_display_window_cascade_all,NULL,
            "~~Cascade all windows","~~Cascada todas ventanas","~~Cascada totes les finestres");
        menu_add_item_menu_tooltip(array_menu_common,"Cascade all windows on the ZX Desktop");
        menu_add_item_menu_ayuda(array_menu_common,"Cascade all windows on the ZX Desktop");
        menu_add_item_menu_shortcut(array_menu_common,'c');
        menu_add_item_menu_se_cerrara(array_menu_common);

        menu_add_item_menu_separator(array_menu_common);

        menu_add_item_menu_en_es_ca(array_menu_common,MENU_OPCION_NORMAL,menu_display_window_maximize_all,NULL,
            "M~~aximize all windows","M~~aximizar todas las ventanas","M~~aximitzar totes les finestres");
        menu_add_item_menu_shortcut(array_menu_common,'a');
        menu_add_item_menu_se_cerrara(array_menu_common);

        menu_add_item_menu_separator(array_menu_common);

        menu_add_item_menu_en_es_ca(array_menu_common,MENU_OPCION_NORMAL,menu_display_window_reduce_all_rearrange,NULL,
            "Reduce+rearrange all windows","Reducir+reubicar todas las ventanas","Reduir+reubicar totes les finestres");
        menu_add_item_menu_se_cerrara(array_menu_common);

        menu_add_item_menu_en_es_ca(array_menu_common,MENU_OPCION_NORMAL,menu_display_window_minimize_all_rearrange,NULL,
            "Minimize+rearrange all windows","Minimizar+reubicar todas ventanas","Minimitzar+reubicar totes les finestres");
        menu_add_item_menu_se_cerrara(array_menu_common);



        menu_add_item_menu_en_es_ca(array_menu_common,MENU_OPCION_NORMAL,menu_display_window_close_all,NULL,
            "Close all windows","Cerrar todas las ventanas","Tancar totes les finestres");
        menu_add_item_menu_se_cerrara(array_menu_common);


        menu_add_item_menu_separator(array_menu_common);

        menu_add_ESC_item(array_menu_common);

        menu_add_item_menu_index_full_path(array_menu_common,"Main Menu-> Windows","Menú Principal-> Windows","Menú Principal-> Windows");

        retorno_menu=menu_dibuja_menu(&windows_opcion_seleccionada,&item_seleccionado,array_menu_common,"Windows Menu","Menú Windows","Menú Windows" );


        if ((item_seleccionado.tipo_opcion&MENU_OPCION_ESC)==0 && retorno_menu>=0) {
            //llamamos por valor de funcion
            if (item_seleccionado.menu_funcion!=NULL) {
                    //printf ("actuamos por funcion\n");
                    item_seleccionado.menu_funcion(item_seleccionado.valor_opcion);

            }
        }

    } while ( (item_seleccionado.tipo_opcion&MENU_OPCION_ESC)==0 && retorno_menu!=MENU_RETORNO_ESC && !salir_todos_menus);




}

#define TEXTADVENTURE_IMAGE_BPM "output_aventure_location_image.bmp"
#define TEXTADVENTURE_IMAGE_BPM_CREATED "output_aventure_location_image.new"

z80_byte *textadv_loc_image_bmp_file_mem=NULL;

//Ultima maquina activa cuando se cargo el teclado. Si cambia maquina, cargar teclado correspondiente
int textadv_loc_last_current_machine=-1;

void menu_textadv_loc_image_load_bmp(void)
{

    //Cargar el archivo bmp
    /*
    Debe ser bmp. 256 colour (indexed).  grabar con no-codificación run lenght,  y no sobreescribir la información de espacio de colores
    */

    char nombrebmp[PATH_MAX];


    strcpy(nombrebmp,TEXTADVENTURE_IMAGE_BPM);

    debug_printf(VERBOSE_DEBUG,"Loading bmp %s",nombrebmp);

    //localizarlo
    char buffer_nombre[PATH_MAX];

    int existe=find_sharedfile(nombrebmp,buffer_nombre);
    if (!existe)  {
            //debug_printf(VERBOSE_ERR,"Unable to find bmp file %s",nombrebmp);
            return;
    }

    //Cargamos el bmp en la paleta primaria
    //Z88 shortcuts: paleta 0
    //Ventana about: paleta 0
    //Keyboard help: paleta 1
    textadv_loc_image_bmp_file_mem=util_load_bmp_file(buffer_nombre,0);


    //if (textadv_loc_bmp_file_mem==NULL) return;




}


int textadv_loc_image_valor_contador_segundo_anterior;

zxvision_window *menu_textadv_loc_image_overlay_window;

//a 0 indica que no se muestra texto
//a 1 indica que se debe mostrar
//a 2 indica que ya se ha mostrado, no hay que escribirlo de nuevo
int menu_textadv_loc_image_show_creating_image=0;

void menu_textadv_loc_image_tell_show_creating_image(void)
{
    menu_textadv_loc_image_show_creating_image=1;
}

void menu_textadv_loc_image_overlay(void)
{

    int recargada_imagen=0;

    if (!si_complete_video_driver() ) return;


    //Cargar bmp si se ha generado archivo

    if (si_existe_archivo(TEXTADVENTURE_IMAGE_BPM_CREATED)) {
        debug_printf(VERBOSE_DEBUG,"A new image has been created");
        util_delete(TEXTADVENTURE_IMAGE_BPM_CREATED);

        debug_printf(VERBOSE_DEBUG,"Loading text adventure location bmp");
        menu_textadv_loc_image_load_bmp();
        recargada_imagen=1;
    }


	//Si no hay archivo bmp cargado
	if (textadv_loc_image_bmp_file_mem==NULL) return;

	menu_speech_set_tecla_pulsada(); //Si no, envia continuamente todo ese texto a speech

    //si ventana minimizada, no ejecutar todo el codigo de overlay
    if (menu_textadv_loc_image_overlay_window->is_minimized) return;

    //printf("Overlay text adventure location image %d\n",contador_segundo);


	zxvision_window *ventana;

	ventana=menu_textadv_loc_image_overlay_window;

    //Solo redibujar cuando se ha refrescado el fondo de texto o si se ha recargado imagen
    if (ventana->has_been_drawn_contents || recargada_imagen ) {

        //esto hara ejecutar esto 5 veces por segundo (lo habitual en muchos de estos que no actualizan siempre es 2 veces por segundo)
        if ( ((contador_segundo%200) == 0 && textadv_loc_image_valor_contador_segundo_anterior!=contador_segundo) ||
            menu_multitarea==0 ||
            recargada_imagen

            ) {

            //printf("Overlay draw location\n");

            textadv_loc_image_valor_contador_segundo_anterior=contador_segundo;

            //Si esta generando imagen, no borrar el texto
            //aqui se puede entrar por ejemplo cuando se esta generando imagen, se ha hecho el zxvision_print_string_defaults_fillspc de abajo
            //y por tanto esta activo el ventana->has_been_drawn_contents
            int mostrar=1;

            //Pero si se ha recargado bmp, entonces si
            if (!recargada_imagen) {
                if (menu_textadv_loc_image_show_creating_image) {
                    mostrar=0;
                }
            }

            if (mostrar) {

                debug_printf(VERBOSE_DEBUG,"Drawing location image");
                //printf ("Refrescando text adventure location image. contador_segundo=%d\n",contador_segundo);

                //Borrar texto anterior de "Recreating image..."
                zxvision_cls(ventana);

                //zoom_x de offset para evitar parpadeo con la linea del recuadro por la izquierda
                screen_render_bmpfile(textadv_loc_image_bmp_file_mem,BMP_INDEX_FIRST_COLOR,ventana,zoom_x,0,0,-1,0);

                menu_textadv_loc_image_show_creating_image=0;

            }

            ventana->has_been_drawn_contents=0;




        }

    }

    if (menu_textadv_loc_image_show_creating_image==1) {
        //printf("Show recreating image text\n");
        menu_textadv_loc_image_show_creating_image=2;

        //borrar cache para quitar restos de overlay y que muestre bien el texto
        ventana->must_clear_cache_on_draw_once=1;
        zxvision_cls(ventana);


        zxvision_print_string_defaults_fillspc(ventana,1,0,"Recreating image...");

        //char temp_buf[100];
        //sprintf(temp_buf,"%d",contador_segundo_infinito);
        //zxvision_print_string_defaults_fillspc(ventana,1,0,temp_buf);

    }

    //Siempre hará el dibujado de contenido para evitar que cuando esta en background, otra ventana por debajo escriba algo,
    //y entonces como esta no redibuja siempre, al no escribir encima, se sobreescribe este contenido con el de otra ventana
    //En ventanas que no escriben siempre su contenido, siempre deberia estar zxvision_draw_window_contents que lo haga siempre
    zxvision_draw_window_contents(ventana);



}


void menu_textadv_loc_image_create_window(zxvision_window *ventana,int x,int y,int ancho,int alto,int is_minimized,int is_maximized,
    int ancho_antes_minimize,int alto_antes_minimize)
{
    zxvision_new_window_gn_cim(ventana,x,y,ancho,alto,ancho-1,alto-2,"Aventure location image","textadvlocimage",is_minimized,is_maximized,ancho_antes_minimize,alto_antes_minimize);

	ventana->can_be_backgrounded=1;

    //Metemos todo el contenido de la ventana con caracter transparente, para que no haya parpadeo
    //en caso de drivers xwindows por ejemplo, pues continuamente redibuja el texto (espacios) y encima el overlay
    //Al meter caracter transparente, el normal_overlay lo ignora y no dibuja ese caracter

    //ya no hace falta transparente debido al nuevo tratamiento de cache de putchar
    //zxvision_fill_window_transparent(ventana);


}



zxvision_window menu_textadv_loc_image_ventana;

void menu_textadv_loc_image(MENU_ITEM_PARAMETERS)
{



	menu_espera_no_tecla();
	menu_reset_counters_tecla_repeticion();

	if (!menu_multitarea) {
			menu_warn_message("This window needs multitask enabled");
			return;
	}

	zxvision_window *ventana;

	ventana=&menu_textadv_loc_image_ventana;

    //IMPORTANTE! no crear ventana si ya existe. Esto hay que hacerlo en todas las ventanas que permiten background.
    //si no se hiciera, se crearia la misma ventana, y en la lista de ventanas activas , al redibujarse,
    //la primera ventana repetida apuntaria a la segunda, que es el mismo puntero, y redibujaria la misma, y se quedaria en bucle colgado
    //zxvision_delete_window_if_exists(ventana);

    //Crear ventana si no existe
    if (!zxvision_if_window_already_exists(ventana)) {


        int x,y,ancho,alto,is_minimized,is_maximized,ancho_antes_minimize,alto_antes_minimize;

        if (!util_find_window_geometry("textadvlocimage",&x,&y,&ancho,&alto,&is_minimized,&is_maximized,&ancho_antes_minimize,&alto_antes_minimize)) {
            //x=menu_origin_x();


            //540x201 es lo que ocupa el bmp de spectrum 48k

            ancho=1+1+540/menu_char_width/zoom_x;

            alto=1+2+201/8/zoom_y;

            //printf ("ancho %d alto %d\n",ancho,alto);

            x=menu_center_x_from_width(ancho);
            y=menu_center_y()-alto/2;

        }



        menu_textadv_loc_image_create_window(ventana,x,y,ancho,alto,is_minimized,is_maximized,ancho_antes_minimize,alto_antes_minimize);

    }

    //Si ya existe, activar esta ventana
    else {

        zxvision_activate_this_window(ventana);
    }

    zxvision_draw_window(ventana);

    int ancho_anterior,alto_anterior;
    zxvision_window_save_size(ventana,&ancho_anterior,&alto_anterior);



    menu_textadv_loc_image_overlay_window=ventana; //Decimos que el overlay lo hace sobre la ventana que tenemos aqui


    textadv_loc_image_valor_contador_segundo_anterior=contador_segundo;

    //Cambiamos funcion overlay de texto de menu
    //Se establece a la de funcion de onda + texto

    //cambio overlay
    zxvision_set_window_overlay(ventana,menu_textadv_loc_image_overlay);

    //Toda ventana que este listada en zxvision_known_window_names_array debe permitir poder salir desde aqui
    //Se sale despues de haber inicializado overlay y de cualquier otra variable que necesite el overlay
    if (zxvision_currently_restoring_windows_on_start) {
        //printf ("Saliendo de ventana ya que la estamos restaurando en startup\n");
        return;
    }

	//Al entrar siempre cargar bmp, esto especialmente es importante por si conmutamos a ventana about o cualquier otra que cargue bmp,
    //que luego al entrar aqui recargue la paleta asociada
    menu_textadv_loc_image_load_bmp();

	z80_byte tecla;

	do {
        tecla=zxvision_common_getkey_refresh();
        zxvision_handle_cursors_pgupdn(ventana,tecla);
        //printf ("tecla: %d\n",tecla);

		if (ventana->visible_height!=alto_anterior || ventana->visible_width!=ancho_anterior) {

            zxvision_window_save_size(ventana,&ancho_anterior,&alto_anterior);

            //Esto evita el parpadeo al redimensionar para hacer mas grande. Llenamos toda la ventana con el transparente,
            //que se hace asi siempre al crearla por primera vez

            //ya no hace falta transparente debido al nuevo tratamiento de cache de putchar
            //zxvision_fill_window_transparent(ventana);

		}
	} while (tecla!=2 && tecla!=3);

	//Gestionar salir con tecla background

	menu_espera_no_tecla(); //Si no, se va al menu anterior.
	//En AY Piano por ejemplo esto no pasa aunque el estilo del menu es el mismo...



	//Grabar geometria ventana
	util_add_window_geometry_compact(ventana);


	if (tecla==3) {
		zxvision_message_put_window_background();
	}

	else {
		zxvision_destroy_window(ventana);
		free(textadv_loc_image_bmp_file_mem);
 	}


}

void menu_text_adventure_tools_location_desc_enable(MENU_ITEM_PARAMETERS)
{
    if (textadv_location_desc_enabled.v) {
        textadv_location_desc_disable();
    }
    else {
        textadv_location_desc_enable();

        //Y esto necesita trap print, lo habilitamos
        if (chardetect_printchar_enabled.v==0) {
            chardetect_printchar_enabled.v=1;
        }
    }
}

void menu_textimage_filter_program(MENU_ITEM_PARAMETERS)
{

    char *filtros[2];

    filtros[0]="";
    filtros[1]=0;



    //guardamos directorio actual
    char directorio_actual[PATH_MAX];
    getcwd(directorio_actual,PATH_MAX);

    //Obtenemos directorio de textimage program
    //si no hay directorio, vamos a rutas predefinidas
    if (textimage_filter_program[0]==0) menu_chdir_sharedfiles();
    else {
        char directorio[PATH_MAX];
        util_get_dir(textimage_filter_program,directorio);
        //printf ("strlen directorio: %d directorio: %s\n",strlen(directorio),directorio);

        //cambiamos a ese directorio, siempre que no sea nulo
        if (directorio[0]!=0) {
            debug_printf (VERBOSE_INFO,"Changing to last directory: %s",directorio);
            zvfs_chdir(directorio);
        }
    }

    int ret;

    ret=menu_filesel("Select Image Program",filtros,textimage_filter_program);
    //volvemos a directorio inicial
    zvfs_chdir(directorio_actual);


    if (ret==1) {
        textimage_filter_program_check_spaces();
    }

    else {
        textimage_filter_program[0]=0;
    }



}

void menu_textimage_max_nochar_value(MENU_ITEM_PARAMETERS)
{
    menu_ventana_scanf_numero_enhanced("Max (in ms)",&max_textadv_location_desc_no_char_counter,6,+200,0,10000,0);
}

//tiempo desde borrado de pantalla hasta fin de localidad
void menu_textimage_max_after_clear_value(MENU_ITEM_PARAMETERS)
{
    menu_ventana_scanf_numero_enhanced("Max (in ms)",&max_textadv_location_desc_counter,6,+200,0,10000,0);
}

void menu_textimage_min_between_images(MENU_ITEM_PARAMETERS)
{
    menu_ventana_scanf_numero_enhanced("Min (in ms)",&textadv_location_desc_last_image_generated_min,6,+1000,0,20000,0);
}


void menu_textimage_detect_location_method(MENU_ITEM_PARAMETERS)
{

    textadv_location_change_method();

}

void menu_text_adventure_tools(MENU_ITEM_PARAMETERS)
{
    menu_item *array_menu_common;
    menu_item item_seleccionado;
    int retorno_menu;


    do {

        menu_add_item_menu_en_es_ca_inicial(&array_menu_common,MENU_OPCION_NORMAL,menu_osd_adventure_keyboard,NULL,
            "On Screen ~~Adventure Keyboard","Teclado de ~~Aventura en Pantalla","Teclat d'~~Aventura a Pantalla");
        menu_add_item_menu_shortcut(array_menu_common,'a');
        menu_add_item_menu_tooltip(array_menu_common,"Open On Screen Adventure Keyboard");
        menu_add_item_menu_ayuda(array_menu_common,"Here you have an on screen keyboard but uses words instead of just letters. "
            "It's useful to play Text Adventures, you can redefine your own words");
        menu_add_item_menu_genera_ventana(array_menu_common);

        menu_add_item_menu_en_es_ca(array_menu_common,MENU_OPCION_NORMAL,menu_unpaws_ungac,NULL,
            "~~Extract words to Adv. Keyb.","~~Extraer palabras a Tecl. Av.","~~Extreure paraules a Tecl. Av.");
        menu_add_item_menu_shortcut(array_menu_common,'e');
        menu_add_item_menu_tooltip(array_menu_common,"Runs the word extractor tool for adventure text games");
        menu_add_item_menu_ayuda(array_menu_common,"It runs the word extractor tool and insert these words on the On Screen Adventure Keyboard. "
            "It can detect words on games written with Quill, Paws, DAAD, and GAC");

        menu_add_item_menu_separator(array_menu_common);



        menu_add_item_menu_en_es_ca(array_menu_common,MENU_OPCION_NORMAL,menu_text_adventure_tools_location_desc_enable,NULL,
            "Location Description Processing","Procesar Descripción Localidad","Processar Descripció Localitat");
        menu_add_item_menu_prefijo_format(array_menu_common,"[%c] ",(textadv_location_desc_enabled.v ? 'X' : ' '));

        if (textadv_location_desc_enabled.v) {

            char string_filterprogram_shown[18];

            if (textimage_filter_program[0]) {
                menu_tape_settings_trunc_name(textimage_filter_program,string_filterprogram_shown,18);
            }

            else {
                sprintf (string_filterprogram_shown,"None");
            }

            menu_add_item_menu_en_es_ca(array_menu_common,MENU_OPCION_NORMAL,menu_textimage_filter_program,NULL,
            "    Converter","    Conversor","    Conversor");
            menu_add_item_menu_sufijo_format(array_menu_common," [%s]",string_filterprogram_shown);



            menu_add_item_menu_en_es_ca(array_menu_common,MENU_OPCION_NORMAL,menu_textimage_detect_location_method,NULL,
            "    Algorithm","    Algoritmo","    Algoritme");
            menu_add_item_menu_sufijo_format(array_menu_common," [%s]",textadv_location_additional_room_change_method_strings[textadv_location_additional_room_change_method]);
            menu_add_item_menu_tooltip(array_menu_common,"How to detect location description text");
            menu_add_item_menu_ayuda(array_menu_common,"How to detect location description text. "
                "Room number method only works for adventures created with Daad, Paws, Quill or GAC");


            menu_add_item_menu_en_es_ca(array_menu_common,MENU_OPCION_NORMAL,menu_textimage_max_nochar_value,NULL,
            "    Minimum no-char","    Mínimo no-char","    Miním no-char");
            menu_add_item_menu_sufijo_format(array_menu_common," [%d ms]",max_textadv_location_desc_no_char_counter);
            menu_add_item_menu_tooltip(array_menu_common,"After that time (in miliseconds) without receiving any character, "
                "we can guess it's the end of the location description. Increase it if the descriptions are not full read");
            menu_add_item_menu_ayuda(array_menu_common,"After that time (in miliseconds) without receiving any character, "
                "we can guess it's the end of the location description. Increase it if the descriptions are not full read");


            menu_add_item_menu_en_es_ca(array_menu_common,MENU_OPCION_NORMAL,menu_textimage_max_after_clear_value,NULL,
            "    Minimum after-room","    Mínimo después-hab","    Miním després-hab");
            menu_add_item_menu_sufijo_format(array_menu_common," [%d ms]",max_textadv_location_desc_counter);
            menu_add_item_menu_tooltip(array_menu_common,"After room change and after that time (in miliseconds), "
                "we can guess it's the end of the location description. Increase it if the descriptions are blank or not full read");
            menu_add_item_menu_ayuda(array_menu_common,"After room change and after that time (in miliseconds), "
                "we can guess it's the end of the location description. Increase it if the descriptions are blank or not full read");



            menu_add_item_menu_en_es_ca(array_menu_common,MENU_OPCION_NORMAL,menu_textimage_min_between_images,NULL,
            "    Minimum between images","    Mínimo entre imágenes","    Miním entre imatges");
            menu_add_item_menu_sufijo_format(array_menu_common," [%d ms]",textadv_location_desc_last_image_generated_min);
            menu_add_item_menu_tooltip(array_menu_common,"Minimum time between every image to avoid too much cost usage by external API");
            menu_add_item_menu_ayuda(array_menu_common,"Minimum time between every image to avoid too much cost usage by external API");


            menu_add_item_menu_en_es_ca(array_menu_common,MENU_OPCION_NORMAL,menu_textadv_loc_image,NULL,
                "    Window ~~image","    Ventana de ~~Imagen","    Finestra d'~~Imatge");
            menu_add_item_menu_shortcut(array_menu_common,'i');
            menu_add_item_menu_tooltip(array_menu_common,"Shows AI generated image for the description of the current location");
            menu_add_item_menu_ayuda(array_menu_common,"Shows AI generated image for the description of the current location");

            menu_add_item_menu_separator(array_menu_common);


            menu_add_item_menu_en_es_ca(array_menu_common,MENU_OPCION_SEPARADOR,NULL,NULL,
            "    Info: Total conversions","    Info: Total conversiones","    Info: Total conversions");
            menu_add_item_menu_sufijo_format(array_menu_common,": %d",textadv_location_total_conversions);


            menu_add_item_menu_separator(array_menu_common);


            menu_add_item_menu_format(array_menu_common,MENU_OPCION_NORMAL,menu_chardetection_settings,NULL,"~~Print char traps");
            menu_add_item_menu_spanish_catalan(array_menu_common,"Traps de im~~presión de caracteres","Traps d'im~~pressió de caràcters");
            menu_add_item_menu_shortcut(array_menu_common,'p');
            menu_add_item_menu_tooltip(array_menu_common,"Settings on capture print character routines");
            menu_add_item_menu_ayuda(array_menu_common,"Settings on capture print character routines");
            menu_add_item_menu_tiene_submenu(array_menu_common);
        }



        menu_add_item_menu_separator(array_menu_common);

        menu_add_ESC_item(array_menu_common);

        retorno_menu=menu_dibuja_menu_no_title_lang(&text_adventure_tools_opcion_seleccionada,&item_seleccionado,array_menu_common,"Text aventure Tools");



        if ((item_seleccionado.tipo_opcion&MENU_OPCION_ESC)==0 && retorno_menu>=0) {
            //llamamos por valor de funcion
            if (item_seleccionado.menu_funcion!=NULL) {
                //printf ("actuamos por funcion\n");
                item_seleccionado.menu_funcion(item_seleccionado.valor_opcion);

            }
        }

    } while ( (item_seleccionado.tipo_opcion&MENU_OPCION_ESC)==0 && retorno_menu!=MENU_RETORNO_ESC && !salir_todos_menus);

}


zxvision_window *menu_video_output_window;


void menu_video_output_overlay(void)
{

    menu_speech_set_tecla_pulsada(); //Si no, envia continuamente todo ese texto a speech

    //si ventana minimizada, no ejecutar todo el codigo de overlay
    if (menu_video_output_window->is_minimized) return;


    if (rainbow_enabled.v) {

        int x,y,xmax,ymax;
        xmax=get_total_ancho_rainbow();
        ymax=get_total_alto_rainbow();
        z80_int *puntero;
        puntero=rainbow_buffer;

        for (y=0;y<ymax;y++) {
            for (x=0;x<xmax;x++) {
                zxvision_putpixel(menu_video_output_window,x,y,*puntero);
                puntero++;
            }
        }

    }


    //Mostrar contenido
    zxvision_draw_window_contents(menu_video_output_window);

}




//Almacenar la estructura de ventana aqui para que se pueda referenciar desde otros sitios
zxvision_window zxvision_window_video_output;


void menu_video_output(MENU_ITEM_PARAMETERS)
{
	menu_espera_no_tecla();

    if (!menu_multitarea) {
        menu_warn_message("This window needs multitask enabled");
        return;
    }

    zxvision_window *ventana;
    ventana=&zxvision_window_video_output;

	//IMPORTANTE! no crear ventana si ya existe. Esto hay que hacerlo en todas las ventanas que permiten background.
	//si no se hiciera, se crearia la misma ventana, y en la lista de ventanas activas , al redibujarse,
	//la primera ventana repetida apuntaria a la segunda, que es el mismo puntero, y redibujaria la misma, y se quedaria en bucle colgado
	//zxvision_delete_window_if_exists(ventana);

    //Crear ventana si no existe
    if (!zxvision_if_window_already_exists(ventana)) {
        int xventana,yventana,ancho_ventana,alto_ventana,is_minimized,is_maximized,ancho_antes_minimize,alto_antes_minimize;

        if (!util_find_window_geometry("videooutput",&xventana,&yventana,&ancho_ventana,&alto_ventana,&is_minimized,&is_maximized,&ancho_antes_minimize,&alto_antes_minimize)) {
            ancho_ventana=30;
            alto_ventana=20;

            xventana=menu_center_x()-ancho_ventana/2;
            yventana=menu_center_y()-alto_ventana/2;
        }


        zxvision_new_window_gn_cim(ventana,xventana,yventana,ancho_ventana,alto_ventana,ancho_ventana-1,alto_ventana-2,"Video Output",
            "videooutput",is_minimized,is_maximized,ancho_antes_minimize,alto_antes_minimize);

        ventana->can_be_backgrounded=1;

    }

    //Si ya existe, activar esta ventana
    else {
        zxvision_activate_this_window(ventana);
    }

	zxvision_draw_window(ventana);

	z80_byte tecla;


	int salir=0;


    menu_video_output_window=ventana; //Decimos que el overlay lo hace sobre la ventana que tenemos aqui


    //cambio overlay
    zxvision_set_window_overlay(ventana,menu_video_output_overlay);


    //Toda ventana que este listada en zxvision_known_window_names_array debe permitir poder salir desde aqui
    //Se sale despues de haber inicializado overlay y de cualquier otra variable que necesite el overlay
    if (zxvision_currently_restoring_windows_on_start) {
            //printf ("Saliendo de ventana ya que la estamos restaurando en startup\n");
            return;
    }

    if (rainbow_enabled.v==0) {
        zxvision_print_string_defaults_fillspc(ventana,1,0,"Real video not enabled");
        zxvision_print_string_defaults_fillspc(ventana,1,1,"Enable it on Setttings->Display");
    }
    else {
        zxvision_cls(ventana);
    }

    do {


		tecla=zxvision_common_getkey_refresh();


        switch (tecla) {

            //Salir con ESC
            case 2:
                salir=1;
            break;

            //O tecla background
            case 3:
                salir=1;
            break;
        }


    } while (salir==0);


	util_add_window_geometry_compact(ventana);

	if (tecla==3) {
		zxvision_message_put_window_background();
	}

	else {

		zxvision_destroy_window(ventana);
	}


}


//menu display settings
void menu_display_settings(MENU_ITEM_PARAMETERS)
{

	menu_item *array_menu_display_settings;
	menu_item item_seleccionado;
	int retorno_menu;
	do {



        //Como no sabemos cual sera el item inicial, metemos este sin asignar, que se sobreescribe en el siguiente menu_add_item_menu
		menu_add_item_menu_inicial(&array_menu_display_settings,"",MENU_OPCION_UNASSIGNED,NULL,NULL);

		if (MACHINE_IS_SPECTRUM || MACHINE_IS_QL) {
			menu_add_item_menu_en_es_ca(array_menu_display_settings,MENU_OPCION_NORMAL,menu_display_load_screen,NULL,
                "~~Load Screen","~~Load Pantalla","~~Load Pantalla");
			menu_add_item_menu_shortcut(array_menu_display_settings,'l');
            menu_add_item_menu_se_cerrara(array_menu_display_settings);
            menu_add_item_menu_genera_ventana(array_menu_display_settings);
		}

        //Al menos la escritura en bmp está permitido para toda máquina que soporte realvideo
		menu_add_item_menu_en_es_ca(array_menu_display_settings,MENU_OPCION_NORMAL,menu_display_save_screen,NULL,
            "~~Save Screen","~~Salvar Pantalla","~~Salvar Pantalla");
		menu_add_item_menu_shortcut(array_menu_display_settings,'s');
        menu_add_item_menu_se_cerrara(array_menu_display_settings);
        menu_add_item_menu_genera_ventana(array_menu_display_settings);
		menu_add_item_menu_tooltip(array_menu_display_settings,"Save screen to disk. BMP format requires to enable real video first");
		menu_add_item_menu_ayuda(array_menu_display_settings,"Save screen to disk. BMP format requires to enable real video first");



        menu_add_item_menu_en_es_ca(array_menu_display_settings,MENU_OPCION_NORMAL,menu_display_quicksave_scr,NULL,
            "Qu~~icksave Screen","Salvado Pantalla ráp~~ido","Salvat Pantalla ràp~~id");
        menu_add_item_menu_se_cerrara(array_menu_display_settings);
        menu_add_item_menu_genera_ventana(array_menu_display_settings);
        menu_add_item_menu_shortcut(array_menu_display_settings,'i');
        menu_add_item_menu_tooltip(array_menu_display_settings,"Save screen to disk quickly");
        menu_add_item_menu_ayuda(array_menu_display_settings,"Save screen to disk quickly. .SCR by default on Spectrum, .BMP on other machines. Name prefix and directory to save are configured on settings->Snapshot");




		menu_add_item_menu_en_es_ca(array_menu_display_settings,MENU_OPCION_NORMAL,menu_view_screen,NULL,
            "~~View Screen","~~Ver Pantalla","~~Veure Pantalla");
		menu_add_item_menu_shortcut(array_menu_display_settings,'v');
        //No cerrar menu al volver de view screen, queremos que se vuelva al menu
        //menu_add_item_menu_se_cerrara(array_menu_display_settings);



        //Todavia no tengo claro del todo para que servira esto, pero de momento renderiza la pantalla
        //cuando real video esta activado
		menu_add_item_menu_en_es_ca(array_menu_display_settings,MENU_OPCION_NORMAL,menu_video_output,NULL,
            "Video Output","Salida de Video","Sortida de Video");
        menu_add_item_menu_genera_ventana(array_menu_display_settings);
        menu_add_item_menu_se_cerrara(array_menu_display_settings);


        menu_add_item_menu_en_es_ca(array_menu_display_settings,MENU_OPCION_NORMAL,menu_display_total_palette,NULL,
            "View ~~Colour Palettes","Ver Paletas de ~~Colores","Veure Paletes de ~~Colors");
        menu_add_item_menu_shortcut(array_menu_display_settings,'c');
        menu_add_item_menu_se_cerrara(array_menu_display_settings);
        menu_add_item_menu_genera_ventana(array_menu_display_settings);
        menu_add_item_menu_tooltip(array_menu_display_settings,"View full palettes or mapped palettes");
        menu_add_item_menu_ayuda(array_menu_display_settings,"You can see in this menu full colour palettes or mapped colour palettes. \n"
                                            "Full colour palettes means all the colours available for a mode, for example 256 colours on ULAPlus.\n"
                                            "Mapped colour palettes means the active palette for a mode, for example 64 colours on ULAPlus.");


       //Teclados en pantalla
        if (MACHINE_IS_SPECTRUM || MACHINE_IS_ZX8081) {
            menu_add_item_menu(array_menu_display_settings,"",MENU_OPCION_SEPARADOR,NULL,NULL);

            menu_add_item_menu_en_es_ca(array_menu_display_settings,MENU_OPCION_NORMAL,menu_onscreen_keyboard,NULL,
                "On Screen ~~Keyboard","Te~~klado en pantalla","Te~~klat a pantalla");
            menu_add_item_menu_shortcut(array_menu_display_settings,'k');
            menu_add_item_menu_se_cerrara(array_menu_display_settings);
            menu_add_item_menu_genera_ventana(array_menu_display_settings);
            menu_add_item_menu_tooltip(array_menu_display_settings,"Open on screen keyboard");
            menu_add_item_menu_ayuda(array_menu_display_settings,"You can also get this pressing F8, only for Spectrum and ZX80/81 machines");
        }

        if (MACHINE_IS_SPECTRUM || MACHINE_IS_ZX8081 || MACHINE_IS_CPC) {

            menu_add_item_menu_en_es_ca(array_menu_display_settings,MENU_OPCION_NORMAL,menu_text_adventure_tools,NULL,
                "~~Text Adventure Tools","Utilidades aventuras de ~~texto","Utilitats aventures de ~~text");
            menu_add_item_menu_shortcut(array_menu_display_settings,'t');
            menu_add_item_menu_tooltip(array_menu_display_settings,"Text Adventure Tools");
            menu_add_item_menu_ayuda(array_menu_display_settings,"Text Adventure Tools");
            menu_add_item_menu_tiene_submenu(array_menu_display_settings);

        }




        menu_add_item_menu(array_menu_display_settings,"",MENU_OPCION_SEPARADOR,NULL,NULL);
        //menu_add_item_menu(array_menu_display_settings,"ESC Back",MENU_OPCION_NORMAL|MENU_OPCION_ESC,NULL,NULL);
		menu_add_ESC_item(array_menu_display_settings);

        //La llamada a menu_add_item_menu_index_full_path lo agrega al principio del array, por tanto lo podemos llamar desde cualquier
        //punto de la creacion del menu. Lo llamo desde aqui porque queda esteticamente mas bonito
        menu_add_item_menu_index_full_path(array_menu_display_settings,"Main Menu-> Display","Menú Principal-> Display","Menú Principal-> Display");

        retorno_menu=menu_dibuja_menu(&display_settings_opcion_seleccionada,&item_seleccionado,array_menu_display_settings,"Display Menu","Menú Display","Menú Display" );



		//NOTA: no llamar por numero de opcion dado que hay opciones que ocultamos (relacionadas con real video)

		if ((item_seleccionado.tipo_opcion&MENU_OPCION_ESC)==0 && retorno_menu>=0) {

			//llamamos por valor de funcion
        	        if (item_seleccionado.menu_funcion!=NULL) {
                	        //printf ("actuamos por funcion\n");
	                        item_seleccionado.menu_funcion(item_seleccionado.valor_opcion);

        	        }
		}

        } while ( (item_seleccionado.tipo_opcion&MENU_OPCION_ESC)==0 && retorno_menu!=MENU_RETORNO_ESC && !salir_todos_menus);



}




void menu_ay_pianokeyboard_insert_inverse(char *origen_orig, int indice)
{
	char cadena_temporal[40];

	char *destino;

	destino=cadena_temporal;

	char *origen;
	origen=origen_orig;

	int i;

    //metemos un ~^ que indica siempre inverso sin bajar letra a minusculas
	for (i=0;*origen;origen++,i++) {
			if (i==indice) {
				*destino++='~';
				*destino++='^';
			}

			*destino++=*origen;
	}

	*destino=0;

	//copiar a cadena original
	strcpy(origen_orig,cadena_temporal);
}




#define PIANO_ZOOM_X audiochip_piano_zoom_x
#define PIANO_ZOOM_Y audiochip_piano_zoom_y

#define AUDIOCHIP_PIANO_LINE_START 1

//1 de margen y otros mas para poder escribir la octava y los cursores
#define AUDIOCHIP_PIANO_COLUMN_START 5


#define AY_PIANO_ANCHO_VENTANA 32

#define AUDIOCHIP_PIANO_ANCHO_UNA_OCTAVA 29


//Escala alto en vertical teclado piano segun si ay chip>2, para que el teclado sea mas pequeñito
int scale_y_chip(int y)
{
	//if (ay_retorna_numero_chips()<3) return y;

	//Si venimos de un Wave Piano, no hay que hacerlo pequeño , aunque tengamos 3 chips de audio, a ese menu no le tiene que afectar
	//if (menu_ay_piano_drawing_wavepiano.v) return y;

    //Dejamos tal cual mismo espacio aunque hayan 3 chips
    return y;


}

//Dice si se muestra piano grafico o de texto.
//Si es un driver de solo texto, mostrar texto
//Si es un driver grafico y setting dice que lo mostremos en texto, mostrar texto
//Si nada de lo demas, mostrar grafico
int si_mostrar_ay_piano_grafico(void)
{
	if (!si_complete_video_driver()) return 0;

	if (!setting_mostrar_ay_piano_grafico.v) return 0;

	return 1;

}

//Retornar separacion entre teclados
int menu_audiochip_piano_get_keys_separation(void)
{
    //Estos incluye son 8 "pixeles" dentro del piano, que se multiplican por PIANO_ZOOM_Y
    //Por tanto si PIANO_ZOOM_Y es 3, cada teclado de piano ocupa 24 pixeles de alto, o sea, 3 lineas de texto
    return 8;

}

//Retorna separacion en lineas de texto entre teclados
int menu_audiochip_piano_get_text_separation_lines(void)
{

    if (!si_mostrar_ay_piano_grafico()) {
        return 3;
    }


    int pixel_separation_lines=menu_audiochip_piano_get_keys_separation()*PIANO_ZOOM_Y;

    //Por suerte esto da multiple de 8 y podemos dividir bien
    int text_separation_lines=pixel_separation_lines/8;

    //Al menos dos lineas de separacion para escribir offset octava y cursores movimiento
    //if (text_separation_lines<2) text_separation_lines=2;

    return text_separation_lines;
}

void menu_ay_pianokeyboard_draw_graphical_piano_draw_pixel_zoom(zxvision_window *ventana,int x,int y,int color)
{


	int offsetx=AUDIOCHIP_PIANO_COLUMN_START*menu_char_width;
	int offsety=scale_y_chip(menu_audiochip_piano_get_keys_separation() )+AUDIOCHIP_PIANO_LINE_START*menu_char_height;

	x=offsetx+x*PIANO_ZOOM_X;
	y=offsety+y*PIANO_ZOOM_Y;

	int xorig=x;
	int zx=0;
	int zy=0;

	for (zy=0;zy<PIANO_ZOOM_Y;zy++) {
		x=xorig;
		for (zx=0;zx<PIANO_ZOOM_X;zx++) {
			//No deberia ser null , pero por si acaso
			if (zxvision_current_window!=NULL) zxvision_putpixel(ventana,x,y,color);

			x++;

		}
		y++;
	}

}



//Basandome en coordenadas basicas sin zoom
void menu_ay_pianokeyboard_draw_graphical_piano_draw_line(zxvision_window *ventana,int x, int y, int stepx, int stepy, int length, int color)
{

	for (;length>0;length--) {
			menu_ay_pianokeyboard_draw_graphical_piano_draw_pixel_zoom(ventana,x,y,color);
			x +=stepx;
			y +=stepy;
	}

}

void menu_ay_piano_graph_dibujar_negra(zxvision_window *ventana,int x, int y,int color)
{
 int alto=4;

	for (alto=0;alto<4;alto++) {
		menu_ay_pianokeyboard_draw_graphical_piano_draw_line(ventana,x, y, +1, 0, 3, color);
		y++;
	}
}


//Como C, F
void menu_ay_piano_graph_dibujar_blanca_izquierda(zxvision_window *ventana,int x, int y,int color)
{
	menu_ay_pianokeyboard_draw_graphical_piano_draw_line(ventana,x, y, 0, +1, scale_y_chip(7), color);
	menu_ay_pianokeyboard_draw_graphical_piano_draw_line(ventana,x+1, y, 0, +1, scale_y_chip(7), color);
	menu_ay_pianokeyboard_draw_graphical_piano_draw_line(ventana,x+2, y+4, 0, +1, scale_y_chip(3), color);
}

//Como D, G, A
void menu_ay_piano_graph_dibujar_blanca_media(zxvision_window *ventana,int x, int y,int color)
{

	menu_ay_pianokeyboard_draw_graphical_piano_draw_line(ventana,x, y+4, 0, +1, scale_y_chip(3), color);
	menu_ay_pianokeyboard_draw_graphical_piano_draw_line(ventana,x+1, y, 0, +1, scale_y_chip(7), color);
	menu_ay_pianokeyboard_draw_graphical_piano_draw_line(ventana,x+2, y+4, 0, +1, scale_y_chip(3), color);
}


//Como E, B
void menu_ay_piano_graph_dibujar_blanca_derecha(zxvision_window *ventana,int x, int y,int color)
{
	menu_ay_pianokeyboard_draw_graphical_piano_draw_line(ventana,x, y+4, 0, +1, scale_y_chip(3), color);
	menu_ay_pianokeyboard_draw_graphical_piano_draw_line(ventana,x+1, y, 0, +1, scale_y_chip(7), color);
	menu_ay_pianokeyboard_draw_graphical_piano_draw_line(ventana,x+2, y, 0, +1, scale_y_chip(7), color);
}

//xposicion vale 0 para el primer teclado mostrado, 1 para el segundo, etc
void menu_ay_pianokeyboard_draw_piano_one_octave(zxvision_window *ventana,int canal,int xposicion)
{
	/*
	Teclado:
	0123456789012345678901234567890

	xxxxxxxxxxxxxxxxxxxxxxxxxxxxx         0
	x  xxx xxx  x  xxx xxx xxx  x         1
	x  xxx xxx  x  xxx xxx xxx  x         2
	x  xxx xxx  x  xxx xxx xxx  x         3
	x  xxx xxx  x  xxx xxx xxx  x         4
	x   x   x   x   x   x   x   x         5
	x   x   x   x   x   x   x   x         6
	x   x   x   x   x   x   x   x         7
    O3                                    8
	0123456789012345678901234567890

    C   D   E   F   G   A   B
Altura, para 2 chips de sonido (6 canales), tenemos maximo 192/6=32
32 de alto maximo, podemos hacer zoom x3 del esquema basico, por tanto tendriamos 8x3x6=144 de alto con 2 chips de sonido


	*/


	//printf ("linea: %d\n",linea);

    int separacion_y_entre_teclados=menu_audiochip_piano_get_keys_separation();

    int ancho_octava=AUDIOCHIP_PIANO_ANCHO_UNA_OCTAVA-1; //-1 para quitar la linea de la derecha de separacion de octava

    int offset_x=xposicion*ancho_octava;


	int ybase=scale_y_chip(separacion_y_entre_teclados)*canal;

	//Recuadro en blanco con brillo
	int x,y;
	for (x=0;x<AUDIOCHIP_PIANO_ANCHO_UNA_OCTAVA;x++) {
		for (y=ybase;y<ybase+scale_y_chip(8);y++) {
			menu_ay_pianokeyboard_draw_graphical_piano_draw_pixel_zoom(ventana,x+offset_x,y,7+8);
		}
	}

	//Linea superior
	menu_ay_pianokeyboard_draw_graphical_piano_draw_line(ventana,0+offset_x, ybase+0, +1, 0, AUDIOCHIP_PIANO_ANCHO_UNA_OCTAVA, 0);

	//Linea vertical izquierda
	menu_ay_pianokeyboard_draw_graphical_piano_draw_line(ventana,0+offset_x, ybase+0, 0, +1, scale_y_chip(8), 0);

	//Linea vertical derecha
	menu_ay_pianokeyboard_draw_graphical_piano_draw_line(ventana,AUDIOCHIP_PIANO_ANCHO_UNA_OCTAVA-1+offset_x, ybase+0, 0, +1, scale_y_chip(8), 0);

	//6 separaciones verticales pequeñas
	int i;
	x=4;
	for (i=0;i<6;i++) {
		menu_ay_pianokeyboard_draw_graphical_piano_draw_line(ventana,x+offset_x, ybase+5, 0, +1, scale_y_chip(3), 0);
		x+=4;
	}

	//Linea vertical central
	menu_ay_pianokeyboard_draw_graphical_piano_draw_line(ventana,12+offset_x, ybase+0, 0, +1, scale_y_chip(8), 0);

	//Y ahora las 5 negras
	x=3;
	for (i=0;i<5;i++) {
		menu_ay_piano_graph_dibujar_negra(ventana,x+offset_x,ybase+1,0);


		x+=4;
		if (i==1) x+=4;  //Saltar posicion donde iria la "tercera" negra
	}




	//Dibujar la linea inferior. Realmente la linea inferior es siempre la linea superior del siguiente canal, excepto en el ultimo canal
	menu_ay_pianokeyboard_draw_graphical_piano_draw_line(ventana,0+offset_x, ybase+scale_y_chip(8), +1, 0, ancho_octava+1, 0);
}



//Octavas de la 0 a la 9
#define AUDIO_CHIP_PIANO_TOTAL_OCTAVAS 10

//Primera octava visible por defecto sera la 2
int audio_chip_piano_offsets_octavas[AUDIO_CHIP_PIANO_TOTAL_OCTAVAS]={
    2,2,2,2,2,2,2,2,2
};

void menu_ay_pianokeyboard_draw_graphical_piano(zxvision_window *ventana,int linea GCC_UNUSED,int canal,char *note,int offset_octava_visible)
{

    int separacion_y_entre_teclados=menu_audiochip_piano_get_keys_separation();


	int ybase=scale_y_chip(separacion_y_entre_teclados)*canal;

	int x;


    int ancho_octava=AUDIOCHIP_PIANO_ANCHO_UNA_OCTAVA-1; //-1 para quitar la linea de la derecha de separacion de octava




    int total_octavas=AUDIO_CHIP_PIANO_TOTAL_OCTAVAS-offset_octava_visible;
    int i;
    for (i=0;i<total_octavas;i++) {
        menu_ay_pianokeyboard_draw_piano_one_octave(ventana,canal,i);
    }


    int nota_final=-1;
    int sostenido;
    int octava;


    get_note_values(note,&nota_final,&sostenido,&octava);


    int xposicion=octava-offset_octava_visible;

    if (xposicion<0) return;

    int offset_x=xposicion*ancho_octava;

	//Y ahora destacar la que se pulsa
	char letra_nota=note[0];
	int es_negra=0;
	if (note[1]=='#') es_negra=1;




    //Ver en cual de las dos octavas que vemos en pantalla esta la tecla pulsada
    //printf("canal %d\n",canal);

	if (es_negra) {

		//determinar posicion x
		switch (letra_nota)
		{
			case 'C':
				x=3;
			break;

			case 'D':
				x=7;
			break;

			case 'F':
				x=15;
			break;

			case 'G':
				x=19;
			break;

			case 'A':
				x=23;
			break;

			default:
				//por si acaso
				x=-1;
			break;
		}
		if (x!=-1) menu_ay_piano_graph_dibujar_negra(ventana,x+offset_x,ybase+1,1); //Color 1 para probar
	}

	else {
		//blancas
		switch (letra_nota)
		{
			case 'C':
			 menu_ay_piano_graph_dibujar_blanca_izquierda(ventana,1+offset_x, ybase+1,1);
			break;

			case 'D':
			//Como D, G, A
			 menu_ay_piano_graph_dibujar_blanca_media(ventana,5+offset_x, ybase+1,1);
			break;

			case 'E':
				menu_ay_piano_graph_dibujar_blanca_derecha(ventana,9+offset_x, ybase+1,1);
			break;

			case 'F':
			 menu_ay_piano_graph_dibujar_blanca_izquierda(ventana,13+offset_x, ybase+1,1);
			break;

			case 'G':
			//Como D, G, A
			 menu_ay_piano_graph_dibujar_blanca_media(ventana,17+offset_x, ybase+1,1);
			break;

			case 'A':
			//Como D, G, A
			 menu_ay_piano_graph_dibujar_blanca_media(ventana,21+offset_x, ybase+1,1);
			break;

			case 'B':
				menu_ay_piano_graph_dibujar_blanca_derecha(ventana,25+offset_x, ybase+1,1);
			break;
		}

	}


}

void menu_ay_pianokeyboard_draw_text_piano_one_octave(zxvision_window *w,int linea,int posicion_x,char *note)
{
	//Forzar a mostrar atajos
	z80_bit antes_menu_writing_inverse_color;
	antes_menu_writing_inverse_color.v=menu_writing_inverse_color.v;

	menu_writing_inverse_color.v=1;

	char linea_negras[40];
	char linea_blancas[40];
												//012345678901
	sprintf (linea_negras, " # #  # # #");
	sprintf (linea_blancas,"C D EF G A B");

	if (note==NULL || note[0]==0) {
	}

	else {
		//Marcar tecla piano pulsada con ~~
		//Interpretar Nota viene como C#4 o C4 por ejemplo
		char letra_nota=note[0];
		int es_negra=0;
		if (note[1]=='#') es_negra=1;

		//TODO: mostramos la octava?

		//Linea negras
		if (es_negra) {
				int indice_negra_marcar=0;
				switch (letra_nota)
				{
					case 'C':
						indice_negra_marcar=1;
					break;

					case 'D':
						indice_negra_marcar=3;
					break;

					case 'F':
						indice_negra_marcar=6;
					break;

					case 'G':
						indice_negra_marcar=8;
					break;

					case 'A':
						indice_negra_marcar=10;
					break;
				}

				//Reconstruimos la cadena introduciendo ~~donde indique el indice
				menu_ay_pianokeyboard_insert_inverse(linea_negras,indice_negra_marcar);
		}

		//Linea blancas
		else {
			int indice_blanca_marcar=0;
			//												//012345678901
			//	sprintf (linea_negras, " # #  # # #");
			//	sprintf (linea_blancas,"C D EF G A B");
			switch (letra_nota)
			{
			  case 'C':
			    indice_blanca_marcar=0;
			  break;

			  case 'D':
			    indice_blanca_marcar=2;
			  break;

			  case 'E':
			    indice_blanca_marcar=4;
			  break;

			  case 'F':
			    indice_blanca_marcar=5;
			  break;

			  case 'G':
			    indice_blanca_marcar=7;
			  break;

			  case 'A':
			    indice_blanca_marcar=9;
			  break;

			  case 'B':
			    indice_blanca_marcar=11;
			  break;
			}

			//Reconstruimos la cadena introduciendo ~~donde indique el indice
			menu_ay_pianokeyboard_insert_inverse(linea_blancas,indice_blanca_marcar);
		}
	}


    int columna=posicion_x;

	zxvision_print_string_defaults(w,columna,linea++,linea_negras);
	zxvision_print_string_defaults(w,columna,linea++,linea_blancas);


	menu_writing_inverse_color.v=antes_menu_writing_inverse_color.v;
}

void menu_ay_pianokeyboard_draw_text_piano(zxvision_window *w,int linea,int canal GCC_UNUSED,char *note,int offset_octava_visible)
{

    linea++;

    int nota_final=-1;
    int sostenido;
    int octava;


    get_note_values(note,&nota_final,&sostenido,&octava);


    int columna_inicio=AUDIOCHIP_PIANO_COLUMN_START+1;



    //13 de separacion en caso de texto
    int ancho_octava=13;



    //int offset_octava_visible=audio_chip_piano_offsets_octavas[canal];

    int total_octavas=AUDIO_CHIP_PIANO_TOTAL_OCTAVAS-offset_octava_visible;
    int i;
    for (i=0;i<total_octavas;i++) {

        int columna=columna_inicio+ancho_octava*i;

        int xposicion_octava=octava-offset_octava_visible;

        //Si octava de la nota coincide con octava escrita
        if (xposicion_octava==i) menu_ay_pianokeyboard_draw_text_piano_one_octave(w,linea,columna,note);

        //Si no, indicar nota vacia para que no la marque
        else menu_ay_pianokeyboard_draw_text_piano_one_octave(w,linea,columna,"");

    }





}



void menu_ay_pianokeyboard_draw_piano(zxvision_window *w,int linea,int canal,char *note,int offset_octava_visible)
{
	if (!si_mostrar_ay_piano_grafico()) {
		menu_ay_pianokeyboard_draw_text_piano(w,linea,canal,note,offset_octava_visible);
	}
	else {
		menu_ay_pianokeyboard_draw_graphical_piano(w,linea,canal,note,offset_octava_visible);
	}
}


zxvision_window *menu_ay_pianokeyboard_overlay_window;



int menu_audiochip_piano_last_offset_window_x=0;

void menu_ay_pianokeyboard_overlay(void)
{




	menu_speech_set_tecla_pulsada(); //Si no, envia continuamente todo ese texto a speech, en el caso que se habilite piano de tipo texto


    //si ventana minimizada, no ejecutar todo el codigo de overlay
    if (menu_ay_pianokeyboard_overlay_window->is_minimized) return;



    int total_chips=audio_get_total_chips();
	//Max 3 ay chips
	if (total_chips>3) total_chips=3;



	int chip;

	int linea=1;

	int canal=0;

	for (chip=0;chip<total_chips;chip++) {


			int freq_a,freq_b,freq_c;


            freq_a=audio_retorna_frecuencia_canal(0,chip);
            freq_b=audio_retorna_frecuencia_canal(1,chip);
            freq_c=audio_retorna_frecuencia_canal(2,chip);

			char nota_a[4];
			sprintf(nota_a,"%s",get_note_name(freq_a) );

			char nota_b[4];
			sprintf(nota_b,"%s",get_note_name(freq_b) );

			char nota_c[4];
			sprintf(nota_c,"%s",get_note_name(freq_c) );



            if (!audio_si_canal_tono(chip,0)) {
                nota_a[0]=0;
                //printf("audio piano canal 0 silencio\n");
            }
            if (!audio_si_canal_tono(chip,1)) {
                nota_b[0]=0;
                //printf("audio piano canal 1 silencio\n");
            }
            if (!audio_si_canal_tono(chip,2)) {
                nota_c[0]=0;
                //printf("audio piano canal 2 silencio\n");
            }

			int incremento_linea=menu_audiochip_piano_get_text_separation_lines();


            int offset_octava_visible;

            offset_octava_visible=audio_chip_piano_offsets_octavas[canal];
			menu_ay_pianokeyboard_draw_piano(menu_ay_pianokeyboard_overlay_window,linea,canal,nota_a,offset_octava_visible);
			linea+=incremento_linea;
			canal++;

            offset_octava_visible=audio_chip_piano_offsets_octavas[canal];
			menu_ay_pianokeyboard_draw_piano(menu_ay_pianokeyboard_overlay_window,linea,canal,nota_b,offset_octava_visible);
			linea+=incremento_linea;
			canal++;

            offset_octava_visible=audio_chip_piano_offsets_octavas[canal];
			menu_ay_pianokeyboard_draw_piano(menu_ay_pianokeyboard_overlay_window,linea,canal,nota_c,offset_octava_visible);
			linea+=incremento_linea;
			canal++;




            int indice_offset_octavas=chip*3;



            int text_separation_lines=menu_audiochip_piano_get_text_separation_lines();

            int linea_texto_octava=2+(chip*(text_separation_lines*3));
            zxvision_print_string_defaults_format(menu_ay_pianokeyboard_overlay_window,1,linea_texto_octava,
                "O%d",audio_chip_piano_offsets_octavas[indice_offset_octavas]);

            linea_texto_octava +=text_separation_lines;
            zxvision_print_string_defaults_format(menu_ay_pianokeyboard_overlay_window,1,linea_texto_octava,
                "O%d",audio_chip_piano_offsets_octavas[indice_offset_octavas+1]);

            linea_texto_octava +=text_separation_lines;
            zxvision_print_string_defaults_format(menu_ay_pianokeyboard_overlay_window,1,linea_texto_octava,
                "O%d",audio_chip_piano_offsets_octavas[indice_offset_octavas+2]);


	}

	zxvision_draw_window_contents(menu_ay_pianokeyboard_overlay_window);

}


zxvision_window zxvision_window_ay_piano;


void menu_audiochip_piano_dec_octave(MENU_ITEM_PARAMETERS)
{
    int canal=valor_opcion;

    int offset_octava=audio_chip_piano_offsets_octavas[canal];

    if (offset_octava>0) {
        offset_octava--;
        audio_chip_piano_offsets_octavas[canal]=offset_octava;
    }
}

void menu_audiochip_piano_inc_octave(MENU_ITEM_PARAMETERS)
{
    int canal=valor_opcion;

    int offset_octava=audio_chip_piano_offsets_octavas[canal];

    if (offset_octava<AUDIO_CHIP_PIANO_TOTAL_OCTAVAS-1) {
        offset_octava++;
        audio_chip_piano_offsets_octavas[canal]=offset_octava;
    }
}

void menu_audiochip_piano_change_zoom(MENU_ITEM_PARAMETERS)
{

    audiochip_piano_zoom_x--;
    audiochip_piano_zoom_y--;

    if (audiochip_piano_zoom_x==0) {
        audiochip_piano_zoom_x=3;
        audiochip_piano_zoom_y=3;
    }


}

void menu_ay_pianokeyboard(MENU_ITEM_PARAMETERS)
{
    menu_espera_no_tecla();

    if (!menu_multitarea) {
        menu_warn_message("This window needs multitask enabled");
        return;
    }

    zxvision_window *ventana;
    ventana=&zxvision_window_ay_piano;

    int total_chips=audio_get_total_chips();
    //Max 3 ay chips
    if (total_chips>3) total_chips=3;

    //IMPORTANTE! no crear ventana si ya existe. Esto hay que hacerlo en todas las ventanas que permiten background.
    //si no se hiciera, se crearia la misma ventana, y en la lista de ventanas activas , al redibujarse,
    //la primera ventana repetida apuntaria a la segunda, que es el mismo puntero, y redibujaria la misma, y se quedaria en bucle colgado
	//zxvision_delete_window_if_exists(ventana);



    //Crear ventana si no existe
    if (!zxvision_if_window_already_exists(ventana)) {

    int xventana,yventana,ancho_ventana,alto_ventana;





    char *titulo_ventana="Audio Chip Piano";

    int is_minimized,is_maximized,ancho_antes_minimize,alto_antes_minimize;


    if (!util_find_window_geometry("aypiano",&xventana,&yventana,&ancho_ventana,&alto_ventana,&is_minimized,&is_maximized,
        &ancho_antes_minimize,&alto_antes_minimize)) {

        if (!si_mostrar_ay_piano_grafico()) {

            ancho_ventana=14;

            if (total_chips==1) {
                alto_ventana=14;
            }
            else if (total_chips==2) {
                alto_ventana=26;
            }

            else {
                alto_ventana=31;
            }

        }

        else {

            ancho_ventana=AY_PIANO_ANCHO_VENTANA;


            int text_separation_lines=menu_audiochip_piano_get_text_separation_lines();
            alto_ventana=text_separation_lines*total_chips*3+AUDIOCHIP_PIANO_LINE_START+4;


        }


        int ancho_titulo=menu_da_ancho_titulo(titulo_ventana);

        //Para que se lea el titulo de la ventana en tamaño por defecto

        if (ancho_ventana<ancho_titulo) ancho_ventana=ancho_titulo;

        xventana=menu_center_x()-ancho_ventana/2;
        yventana=menu_center_y()-alto_ventana/2;


    }


    //Suficiente para que quepan todas las octavas, y texto de octava y cursores
    int total_width=5+AUDIOCHIP_PIANO_ANCHO_UNA_OCTAVA*PIANO_ZOOM_X*AUDIO_CHIP_PIANO_TOTAL_OCTAVAS/menu_char_width;



    zxvision_new_window_gn_cim(ventana,xventana,yventana,ancho_ventana,alto_ventana,total_width,alto_ventana-2,titulo_ventana,"aypiano",
        is_minimized,is_maximized,ancho_antes_minimize,alto_antes_minimize);

    ventana->can_be_backgrounded=1;

    }

    //Si ya existe, activar esta ventana
    else {

        zxvision_activate_this_window(ventana);
    }


    zxvision_draw_window(ventana);



    menu_ay_pianokeyboard_overlay_window=ventana;


    //Cambiamos funcion overlay de texto de menu
    //Se establece a la de funcion de piano + texto

    //cambio overlay
    zxvision_set_window_overlay(ventana,menu_ay_pianokeyboard_overlay);



    //int valor_contador_segundo_anterior;

    //valor_contador_segundo_anterior=contador_segundo;


    //Toda ventana que este listada en zxvision_known_window_names_array debe permitir poder salir desde aqui
    //Se sale despues de haber inicializado overlay y de cualquier otra variable que necesite el overlay
    if (zxvision_currently_restoring_windows_on_start) {
            //printf ("Saliendo de ventana ya que la estamos restaurando en startup\n");
            return;
    }




    menu_item *array_menu_common;
    menu_item item_seleccionado;
    int retorno_menu;
    do {

        //borrar cache por si hay restos de mover offsets octavas
        ventana->must_clear_cache_on_draw_once=1;
        //borrar ventana por si hay restos de texto al cambiar zoom
        zxvision_cls(ventana);




        menu_add_item_menu_inicial(&array_menu_common,"",MENU_OPCION_UNASSIGNED,NULL,NULL);


        if (si_mostrar_ay_piano_grafico() ) {
            menu_add_item_menu_format(array_menu_common,MENU_OPCION_NORMAL,menu_audiochip_piano_change_zoom,NULL,
                "[%d] Zoom",audiochip_piano_zoom_x);
            menu_add_item_menu_tabulado(array_menu_common,1,0);
        }


        int text_separation_lines=menu_audiochip_piano_get_text_separation_lines();


        int i;
        int linea=AUDIOCHIP_PIANO_LINE_START+1;

        for (i=0;i<total_chips*3;i++) {

            menu_add_item_menu_format(array_menu_common,MENU_OPCION_NORMAL,menu_audiochip_piano_dec_octave,NULL,"<");
            menu_add_item_menu_tabulado(array_menu_common,3,linea);
            menu_add_item_menu_valor_opcion(array_menu_common,i);

            menu_add_item_menu_format(array_menu_common,MENU_OPCION_NORMAL,menu_audiochip_piano_inc_octave,NULL,">");
            menu_add_item_menu_tabulado(array_menu_common,4,linea);
            menu_add_item_menu_valor_opcion(array_menu_common,i);


            linea +=text_separation_lines;

        }

        //Nombre de ventana solo aparece en el caso de stdout
        retorno_menu=menu_dibuja_menu_no_title_lang(&audio_visual_realtape_opcion_seleccionada,&item_seleccionado,array_menu_common,"Audio Chip Piano" );

        if (retorno_menu!=MENU_RETORNO_BACKGROUND) {

                //En caso de menus tabulados, es responsabilidad de este de borrar la ventana

                if ((item_seleccionado.tipo_opcion&MENU_OPCION_ESC)==0 && retorno_menu>=0) {
                        //llamamos por valor de funcion
                        if (item_seleccionado.menu_funcion!=NULL) {
                                //printf ("actuamos por funcion\n");
                                item_seleccionado.menu_funcion(item_seleccionado.valor_opcion);

                        }
                }
        }

    } while ( (item_seleccionado.tipo_opcion&MENU_OPCION_ESC)==0 && retorno_menu!=MENU_RETORNO_ESC && !salir_todos_menus && retorno_menu!=MENU_RETORNO_BACKGROUND);





    util_add_window_geometry_compact(ventana);

	if (retorno_menu==MENU_RETORNO_BACKGROUND) {
        zxvision_message_put_window_background();
	}

	else {

		zxvision_destroy_window(ventana);
	}



	menu_espera_no_tecla();

	/* Nota:
	Creo que este es de los pocos casos en que llamamos a menu_espera_no_tecla al salir, por dos razones:

	1) si no se hiciera, cuando hay text-to-speech+also send menu, la tecla ESC se suele escalar hacia abajo, probablemente porque activa
	variable menu_speech_tecla_pulsada=1
	Probablemente habria que llamar siempre a menu_espera_no_tecla(); al finalizar ventanas que no estan gestionadas por menu_dibuja_menu


	2) si no se hiciera, saldria con menu_speech_tecla_pulsada=1, y la tecla pulsada utilizada para salir de esta ventana (ESC),
	acaba pasando al menu anterior, cerrando el menu directamente. Esto solo pasa si no hay text-to-speech+also send menu
	No se muy bien porque solo sucede en este caso, quiza es porque estamos mostrando texto directamente en la funcion overlay y
	de manera muy rapida. Esto es lo mismo que en el menu de Wave Piano


	*/

}


zxvision_window *menu_beeper_pianokeyboard_overlay_window;

int menu_beeper_piano_offset_octava_visble=2;

void menu_beeper_pianokeyboard_overlay(void)
{


	menu_speech_set_tecla_pulsada(); //Si no, envia continuamente todo ese texto a speech


    //si ventana minimizada, no ejecutar todo el codigo de overlay
    if (menu_beeper_pianokeyboard_overlay_window->is_minimized) return;




	int linea=1;

	int canal=0;

    audiobuffer_stats audiostats;
    audio_get_audiobuffer_stats(&audiostats);


    int frecuencia=audiostats.frecuencia;



    int freq_a=frecuencia;


    char nota_a[4];
    sprintf(nota_a,"%s",get_note_name(freq_a) );

    //Si no hay sonido, suele dar frecuencia 5 o menos
    if (frecuencia<=5) nota_a[0]=0;


    menu_ay_pianokeyboard_draw_piano(menu_beeper_pianokeyboard_overlay_window,linea,canal,nota_a,menu_beeper_piano_offset_octava_visble);



    int linea_texto_octava=2;
    zxvision_print_string_defaults_format(menu_beeper_pianokeyboard_overlay_window,1,linea_texto_octava,
        "O%d",menu_beeper_piano_offset_octava_visble);


    char buffer_texto[40];

    if (nota_a[0]!=0) {
        sprintf (buffer_texto,"%d Hz (%s) ",frecuencia,nota_a);
    }

    else strcpy (buffer_texto,"             ");


    zxvision_print_string_defaults(menu_beeper_pianokeyboard_overlay_window,1,6,buffer_texto);



	zxvision_draw_window_contents(menu_beeper_pianokeyboard_overlay_window);

}



zxvision_window zxvision_menu_beeper_pianokeyboard;

void menu_beeper_piano_dec_octave(MENU_ITEM_PARAMETERS)
{

    if (menu_beeper_piano_offset_octava_visble>0) {
        menu_beeper_piano_offset_octava_visble--;
    }
}

void menu_beeper_piano_inc_octave(MENU_ITEM_PARAMETERS)
{

    if (menu_beeper_piano_offset_octava_visble<AUDIO_CHIP_PIANO_TOTAL_OCTAVAS-1) {
        menu_beeper_piano_offset_octava_visble++;
    }
}

void menu_beeper_pianokeyboard(MENU_ITEM_PARAMETERS)
{
	menu_espera_no_tecla();



	if (!menu_multitarea) {
		menu_warn_message("This window needs multitask enabled");
		return;
	}

	zxvision_window *ventana;
	ventana=&zxvision_menu_beeper_pianokeyboard;

    //IMPORTANTE! no crear ventana si ya existe. Esto hay que hacerlo en todas las ventanas que permiten background.
    //si no se hiciera, se crearia la misma ventana, y en la lista de ventanas activas , al redibujarse,
    //la primera ventana repetida apuntaria a la segunda, que es el mismo puntero, y redibujaria la misma, y se quedaria en bucle colgado
	//zxvision_delete_window_if_exists(ventana);



	//Como si fuera 1 solo chip

    //Crear ventana si no existe
    if (!zxvision_if_window_already_exists(ventana)) {


        int xventana,yventana,ancho_ventana,alto_ventana,is_minimized,is_maximized,ancho_antes_minimize,alto_antes_minimize;
        char *titulo_ventana="Wave Piano";
        if (!util_find_window_geometry("wavepiano",&xventana,&yventana,&ancho_ventana,&alto_ventana,&is_minimized,&is_maximized,&ancho_antes_minimize,&alto_antes_minimize)) {
            if (!si_mostrar_ay_piano_grafico()) {
                ancho_ventana=14;
                alto_ventana=9;

            }

            else {
                ancho_ventana=AY_PIANO_ANCHO_VENTANA;
                int text_separation_lines=menu_audiochip_piano_get_text_separation_lines();
                alto_ventana=text_separation_lines+AUDIOCHIP_PIANO_LINE_START+4+1;        //+1 para indicar Hz de la nota

            }

            ancho_antes_minimize=ancho_ventana;
            alto_antes_minimize=alto_ventana;


            int ancho_titulo=menu_da_ancho_titulo(titulo_ventana);

            //Para que se lea el titulo de la ventana cuando tamaño por defecto
            if (ancho_ventana<ancho_titulo) ancho_ventana=ancho_titulo;

            xventana=menu_center_x()-ancho_ventana/2;
            yventana=menu_center_y()-alto_ventana/2;

        }




        //Suficiente para que quepan todas las octavas, y texto de octava y cursores
        int total_width=5+AUDIOCHIP_PIANO_ANCHO_UNA_OCTAVA*PIANO_ZOOM_X*AUDIO_CHIP_PIANO_TOTAL_OCTAVAS/menu_char_width;

        zxvision_new_window_gn_cim(ventana,xventana,yventana,ancho_ventana,alto_ventana,total_width,alto_ventana-2,titulo_ventana,
                    "wavepiano",is_minimized,is_maximized,ancho_antes_minimize,alto_antes_minimize);


        ventana->can_be_backgrounded=1;

    }


    //Si ya existe, activar esta ventana
    else {
        zxvision_activate_this_window(ventana);
    }


	zxvision_draw_window(ventana);

    menu_beeper_pianokeyboard_overlay_window=ventana;

	//Cambiamos funcion overlay de texto de menu
	//Se establece a la de funcion de piano + texto

    //cambio overlay
    zxvision_set_window_overlay(ventana,menu_beeper_pianokeyboard_overlay);




	//int valor_contador_segundo_anterior;

	//valor_contador_segundo_anterior=contador_segundo;


    //Toda ventana que este listada en zxvision_known_window_names_array debe permitir poder salir desde aqui
    //Se sale despues de haber inicializado overlay y de cualquier otra variable que necesite el overlay
    if (zxvision_currently_restoring_windows_on_start) {
        //printf ("Saliendo de ventana ya que la estamos restaurando en startup\n");
        return;
    }


    menu_item *array_menu_common;
    menu_item item_seleccionado;
    int retorno_menu;
    do {

        //borrar cache por si hay restos de mover offsets octavas
        ventana->must_clear_cache_on_draw_once=1;
        //borrar ventana por si hay restos de texto al cambiar zoom
        zxvision_cls(ventana);



        menu_add_item_menu_inicial(&array_menu_common,"",MENU_OPCION_UNASSIGNED,NULL,NULL);


        if (si_mostrar_ay_piano_grafico() ) {
            menu_add_item_menu_format(array_menu_common,MENU_OPCION_NORMAL,menu_audiochip_piano_change_zoom,NULL,
                "[%d] Zoom",audiochip_piano_zoom_x);
            menu_add_item_menu_tabulado(array_menu_common,1,0);
        }


        int text_separation_lines=menu_audiochip_piano_get_text_separation_lines();


        int linea=AUDIOCHIP_PIANO_LINE_START+1;



        menu_add_item_menu_format(array_menu_common,MENU_OPCION_NORMAL,menu_beeper_piano_dec_octave,NULL,"<");
        menu_add_item_menu_tabulado(array_menu_common,3,linea);

        menu_add_item_menu_format(array_menu_common,MENU_OPCION_NORMAL,menu_beeper_piano_inc_octave,NULL,">");
        menu_add_item_menu_tabulado(array_menu_common,4,linea);


        linea +=text_separation_lines;



        //Nombre de ventana solo aparece en el caso de stdout
        retorno_menu=menu_dibuja_menu_no_title_lang(&audio_visual_realtape_opcion_seleccionada,&item_seleccionado,array_menu_common,"Wave Piano" );

        if (retorno_menu!=MENU_RETORNO_BACKGROUND) {

                //En caso de menus tabulados, es responsabilidad de este de borrar la ventana

                if ((item_seleccionado.tipo_opcion&MENU_OPCION_ESC)==0 && retorno_menu>=0) {
                        //llamamos por valor de funcion
                        if (item_seleccionado.menu_funcion!=NULL) {
                                //printf ("actuamos por funcion\n");
                                item_seleccionado.menu_funcion(item_seleccionado.valor_opcion);

                        }
                }
        }

    } while ( (item_seleccionado.tipo_opcion&MENU_OPCION_ESC)==0 && retorno_menu!=MENU_RETORNO_ESC && !salir_todos_menus && retorno_menu!=MENU_RETORNO_BACKGROUND);





    util_add_window_geometry_compact(ventana);

	if (retorno_menu==MENU_RETORNO_BACKGROUND) {
        zxvision_message_put_window_background();
	}

	else {

		zxvision_destroy_window(ventana);
	}




	menu_espera_no_tecla();


	/* Nota:
	Creo que este es de los pocos casos en que llamamos a menu_espera_no_tecla al salir,
	si no se hiciera, saldria con menu_speech_tecla_pulsada=1, y la tecla pulsada utilizada para salir de esta ventana (ESC),
	acaba pasando al menu anterior, cerrando el menu directamente. Esto solo pasa si no hay text-to-speech+also send menu
	No se muy bien porque solo sucede en este caso, quiza es porque estamos mostrando texto directamente en la funcion overlay y
	de manera muy rapida
	*/

}


void menu_debug_msx_memory_info_slot_segment(MENU_ITEM_PARAMETERS)
{
	//Le indicamos el valor de slot y segmento, codificandolo en valor hexadecimal: slot*16+segment

	int slot=(valor_opcion>>4) & 0xF;
	int segment=valor_opcion & 0xF;

	int tipo=msx_memory_slots[slot][segment];

	int inicio_bloque=segment*16384;
	int fin_bloque=((segment+1)*16384)-1;

	if (MACHINE_IS_SVI) {
		inicio_bloque=segment*32768;
		fin_bloque=((segment+1)*32768)-1;
	}
	else {
	inicio_bloque=segment*16384;
	fin_bloque=((segment+1)*16384)-1;
	}





	if (MACHINE_IS_SVI) {
		char buffer_mem_type[32];
		char long_buffer_mem_type[32];
		svi_get_string_memory_slot(buffer_mem_type,long_buffer_mem_type,slot,segment);

		menu_generic_message_format("Block info","Slot: %d Segment: %d Type: %s Uses: %04XH-%04XH"
			,slot,segment,long_buffer_mem_type,inicio_bloque,fin_bloque);
	}

	else {
		//MSX

		menu_generic_message_format("Block info","Slot: %d Segment: %d Type: %s Uses: %04XH-%04XH"
			,slot,segment,msx_get_string_memory_type(tipo),inicio_bloque,fin_bloque);
	}


}


void menu_debug_msx_svi_memory_info(MENU_ITEM_PARAMETERS)
{

	int total_segmentos=4-1;

	if (MACHINE_IS_SVI) total_segmentos=2-1;

	int ancho_ventana=32;
	int alto_ventana=7+(total_segmentos+1)*2;


	int xventana=menu_center_x()-ancho_ventana/2;
	int yventana=menu_center_y()-alto_ventana/2;

	zxvision_window ventana;

	zxvision_new_window(&ventana,xventana,yventana,ancho_ventana,alto_ventana,
                                                        ancho_ventana-1,alto_ventana-2,"Memory Info");

	//Dado que es una variable local, siempre podemos usar este nombre array_menu_common
	menu_item *array_menu_common;
	menu_item item_seleccionado;
	int retorno_menu;

	int comun_opcion_seleccionada=0;


	//Donde van los bloques

	int inicio_bloque_x=8;
	int inicio_bloque_y=2;
	int ancho_bloque=6;

	int linea=inicio_bloque_y;

	if (!MACHINE_IS_SVI) {
	zxvision_print_string_defaults(&ventana,1,linea,"C000H");
	linea+=2;
	}

	zxvision_print_string_defaults(&ventana,1,linea,"8000H");
	linea+=2;

	if (!MACHINE_IS_SVI) {
	zxvision_print_string_defaults(&ventana,1,linea,"4000H");
	linea+=2;
	}

	zxvision_print_string_defaults(&ventana,1,linea,"0000H");
	linea+=2;

	do {



		menu_add_item_menu_inicial_format(&array_menu_common,MENU_OPCION_NORMAL,NULL,NULL,"       Slot0 Slot1 Slot2 Slot3");
		menu_add_item_menu_tabulado(array_menu_common,1,0);

		int slot, segment;

		////slots asignados, y sus 4 segmentos
//tipos: rom, ram, vacio
//int msx_memory_slots[4][4];
/*
#define MSX_SLOT_MEMORY_TYPE_ROM 0
#define MSX_SLOT_MEMORY_TYPE_RAM 1
#define MSX_SLOT_MEMORY_TYPE_EMPTY 2
*/

		//SVI:
		//Siempre estan los slots fijos en svi
/*
  FFFF      BANK 02 RAM     |      BANK 12 CARTRIDGE ROM    |   BANK 22 RAM     |       BANK 32 RAM
  8000


  7FFF      BANK 01 ROM     |      BANK 11 CARTRIDGE ROM    |   BANK 21 RAM     |       BANK 31 RAM
  0000
*/



		for (segment=total_segmentos;segment>=0;segment--) {
			for (slot=0;slot<4;slot++) {


				char buffer_mem_type[32];


				if (MACHINE_IS_SVI) {
					svi_get_string_memory_slot(buffer_mem_type,NULL,slot,segment);
				}
				else {
					//MSX

				int tipo=msx_memory_slots[slot][segment];

				strcpy (buffer_mem_type,msx_get_string_memory_type(tipo));


				}

				menu_add_item_menu_format(array_menu_common,MENU_OPCION_NORMAL,menu_debug_msx_memory_info_slot_segment,NULL,buffer_mem_type);

				int coordenada_y;


				coordenada_y=inicio_bloque_y+(total_segmentos-segment)*2;


				menu_add_item_menu_tabulado(array_menu_common,inicio_bloque_x+slot*ancho_bloque,coordenada_y);

				//Le indicamos el valor de slot y segmento, codificandolo en valor hexadecimal: slot*16+segment

				int valor_opcion=slot*16+segment;
				menu_add_item_menu_valor_opcion(array_menu_common,valor_opcion);
			}
		}




		//if (!total_ventanas) menu_add_item_menu_format(array_menu_common,MENU_OPCION_NORMAL,NULL,NULL,"(Empty)");

		//menu_add_item_menu_separator(array_menu_common);

		menu_add_ESC_item(array_menu_common);
		menu_add_item_menu_tabulado(array_menu_common,1,alto_ventana-4);

		retorno_menu=menu_dibuja_menu_no_title_lang(&comun_opcion_seleccionada,&item_seleccionado,array_menu_common,"Memory Info");


			if ((item_seleccionado.tipo_opcion&MENU_OPCION_ESC)==0 && retorno_menu>=0) {
					//llamamos por valor de funcion
					if (item_seleccionado.menu_funcion!=NULL) {
							//printf ("actuamos por funcion\n");
							item_seleccionado.menu_funcion(item_seleccionado.valor_opcion);

					}
			}

    } while ( (item_seleccionado.tipo_opcion&MENU_OPCION_ESC)==0 && retorno_menu!=MENU_RETORNO_ESC && !salir_todos_menus);

    //En caso de menus tabulados, suele ser necesario esto. Si no, la ventana se quedaria visible


                                //En caso de menus tabulados, es responsabilidad de este de liberar ventana
                zxvision_destroy_window(&ventana);

}


void menu_debug_tsconf_tbblue_msx_cpc_debug_borders(MENU_ITEM_PARAMETERS)
{
	cpc_debug_borders.v ^=1;
}


void menu_debug_sprite_mangement_disable_change(MENU_ITEM_PARAMETERS)
{
    debug_tbblue_sprite_visibility[valor_opcion] ^=1;
}


void menu_debug_sprite_mangement_disable(MENU_ITEM_PARAMETERS)
{



	int ancho_ventana=34;
	int alto_ventana=24;

    //Nota: esta ventana es de 34 de ancho, para dar margen izquierda/derecha,
    //pues los 4 indices por linea ya ocupan 8x4=32
    //por tanto con border disabled, zx desktop disabled y menu char width=8, la ventana no caberia entera, saldra cortada parte por la derecha
    //pero no es lo habitual, al menos habra el border activado (en el caso de tbblue, se fuerza siempre border)


	int xventana=menu_center_x()-ancho_ventana/2;
	int yventana=menu_center_y()-alto_ventana/2;

	zxvision_window ventana;


    int total_height=TBBLUE_MAX_SPRITES/4;

	zxvision_new_window(&ventana,xventana,yventana,ancho_ventana,alto_ventana,
                                                        ancho_ventana-1,total_height,"Sprite disable");

	//Dado que es una variable local, siempre podemos usar este nombre array_menu_common
	menu_item *array_menu_common;
	menu_item item_seleccionado;
	int retorno_menu;

	int comun_opcion_seleccionada=0;




	do {



        menu_add_item_menu_inicial(&array_menu_common,"",MENU_OPCION_UNASSIGNED,NULL,NULL);

		int sprite;


		for (sprite=0;sprite<TBBLUE_MAX_SPRITES;sprite++) {




            menu_add_item_menu_format(array_menu_common,MENU_OPCION_NORMAL,menu_debug_sprite_mangement_disable_change,NULL,
                "[%c] %3d",(debug_tbblue_sprite_visibility[sprite] ? 'X' : ' '),sprite);

            menu_add_item_menu_tabulado(array_menu_common,1+(sprite %4)*8,sprite/4);

            menu_add_item_menu_valor_opcion(array_menu_common,sprite);

		}




		//menu_add_ESC_item(array_menu_common);
		//menu_add_item_menu_tabulado(array_menu_common,1,alto_ventana-4);

		retorno_menu=menu_dibuja_menu_no_title_lang(&comun_opcion_seleccionada,&item_seleccionado,array_menu_common,"Sprite disable");


			if ((item_seleccionado.tipo_opcion&MENU_OPCION_ESC)==0 && retorno_menu>=0) {
					//llamamos por valor de funcion
					if (item_seleccionado.menu_funcion!=NULL) {
							//printf ("actuamos por funcion\n");
							item_seleccionado.menu_funcion(item_seleccionado.valor_opcion);

					}
			}

    } while ( (item_seleccionado.tipo_opcion&MENU_OPCION_ESC)==0 && retorno_menu!=MENU_RETORNO_ESC && !salir_todos_menus);

    //En caso de menus tabulados, suele ser necesario esto. Si no, la ventana se quedaria visible


    //En caso de menus tabulados, es responsabilidad de este de liberar ventana
    zxvision_destroy_window(&ventana);

}

void menu_debug_tsconf_tbblue_msx_copper(MENU_ITEM_PARAMETERS)
{
    tbblue_force_disable_cooper.v ^=1;
}


void menu_debug_tsconf_tbblue_msx(MENU_ITEM_PARAMETERS)
{
        menu_item *array_menu_debug_tsconf_tbblue_msx;
        menu_item item_seleccionado;
	int retorno_menu;
        do {

/*
-con el nuevo comportamiento de menus en ZEsarUX X
*si no se hace lo contrario, al pulsar ESC en un menu (logicamente tambien los tabulados) cerrara todo
*en debug msx por ejemplo, se juntan varias ventanas/menus que tienen comportamientos distintos al pulsar ESC:
 - video info, sprite navigator, tile navigator es una ventana no menu, por tanto al pulsar ESC vuelve a debug msx
 - video layers, memory info son menus tabulados, por tanto al pulsar ESC cierran todos menus
 que habria que hacer en este caso? que en todas cierren todos los menus? que todas vuelvan a debug msx?

 ->pues todos deberian cerrar todos menus, pues para el usuario son ventanas tal cual y no menus de opciones
 por tanto meto menu_add_item_menu_se_cerrara en todos los items, incluso los que son de menus, en que implicitamente
 el sistema de menus ya cerrara todo al pulsar ESC, pero asi queda mas limpio y consistente aqui
*/

		menu_add_item_menu_inicial_format(&array_menu_debug_tsconf_tbblue_msx,MENU_OPCION_NORMAL,menu_debug_tsconf_tbblue_msx_videoregisters,NULL,"Video ~~Info");
		menu_add_item_menu_shortcut(array_menu_debug_tsconf_tbblue_msx,'i');
        menu_add_item_menu_add_flags(array_menu_debug_tsconf_tbblue_msx,MENU_ITEM_FLAG_GENERA_VENTANA | MENU_ITEM_FLAG_SE_CERRARA);

        if (MACHINE_IS_CPC && rainbow_enabled.v) {
            menu_add_item_menu(array_menu_debug_tsconf_tbblue_msx,"",MENU_OPCION_SEPARADOR,NULL,NULL);

            menu_add_item_menu_format(array_menu_debug_tsconf_tbblue_msx,MENU_OPCION_NORMAL,menu_debug_tsconf_tbblue_msx_cpc_debug_borders,NULL,"[%c] Debug borders",
                (cpc_debug_borders.v==1 ? 'X' : ' ') );

        }


        if (!MACHINE_IS_CPC) {
		//menu_add_item_menu_format(array_menu_debug_tsconf_tbblue_msx,MENU_OPCION_NORMAL,menu_tsconf_layer_settings,NULL,"Video ~~Layers");
        menu_add_item_menu_format(array_menu_debug_tsconf_tbblue_msx,MENU_OPCION_NORMAL,menu_video_layers,NULL,"Video ~~Layers");
		menu_add_item_menu_shortcut(array_menu_debug_tsconf_tbblue_msx,'l');
        menu_add_item_menu_add_flags(array_menu_debug_tsconf_tbblue_msx,MENU_ITEM_FLAG_GENERA_VENTANA | MENU_ITEM_FLAG_SE_CERRARA);
        }

        if (!MACHINE_IS_CPC) {
		menu_add_item_menu_format(array_menu_debug_tsconf_tbblue_msx,MENU_OPCION_NORMAL,menu_debug_tsconf_tbblue_msx_spritenav,NULL,"~~Sprite navigator");
		menu_add_item_menu_shortcut(array_menu_debug_tsconf_tbblue_msx,'s');
        menu_add_item_menu_add_flags(array_menu_debug_tsconf_tbblue_msx,MENU_ITEM_FLAG_GENERA_VENTANA | MENU_ITEM_FLAG_SE_CERRARA);
        }

        if (MACHINE_IS_TBBLUE) {
		    menu_add_item_menu_format(array_menu_debug_tsconf_tbblue_msx,MENU_OPCION_NORMAL,menu_debug_sprite_mangement_disable,NULL,"Sprite ~~disabling");
		    menu_add_item_menu_shortcut(array_menu_debug_tsconf_tbblue_msx,'d');
            menu_add_item_menu_add_flags(array_menu_debug_tsconf_tbblue_msx,MENU_ITEM_FLAG_GENERA_VENTANA | MENU_ITEM_FLAG_SE_CERRARA);
        }

		if (MACHINE_IS_TSCONF || MACHINE_IS_TBBLUE || MACHINE_HAS_VDP_9918A) {
			menu_add_item_menu_format(array_menu_debug_tsconf_tbblue_msx,MENU_OPCION_NORMAL,menu_debug_tsconf_tbblue_msx_tilenav,NULL,"~~Tile navigator");
			menu_add_item_menu_shortcut(array_menu_debug_tsconf_tbblue_msx,'t');
            menu_add_item_menu_add_flags(array_menu_debug_tsconf_tbblue_msx,MENU_ITEM_FLAG_GENERA_VENTANA | MENU_ITEM_FLAG_SE_CERRARA);
		}

        if (MACHINE_IS_TBBLUE) {
            menu_add_item_menu(array_menu_debug_tsconf_tbblue_msx,"",MENU_OPCION_SEPARADOR,NULL,NULL);
            menu_add_item_menu_format(array_menu_debug_tsconf_tbblue_msx,MENU_OPCION_NORMAL,menu_debug_tsconf_tbblue_msx_copper,NULL,"[%c] Copper enabled",
                            (tbblue_force_disable_cooper.v ? ' ' : 'X' ));
        }

		if (MACHINE_IS_MSX || MACHINE_IS_SVI) {
			menu_add_item_menu_format(array_menu_debug_tsconf_tbblue_msx,MENU_OPCION_NORMAL,menu_debug_msx_svi_memory_info,NULL,"~~Memory Info");
			menu_add_item_menu_shortcut(array_menu_debug_tsconf_tbblue_msx,'m');
            menu_add_item_menu_add_flags(array_menu_debug_tsconf_tbblue_msx,MENU_ITEM_FLAG_GENERA_VENTANA | MENU_ITEM_FLAG_SE_CERRARA);
		}

                menu_add_item_menu(array_menu_debug_tsconf_tbblue_msx,"",MENU_OPCION_SEPARADOR,NULL,NULL);
                //menu_add_item_menu(array_menu_debug_tsconf_tbblue_msx,"ESC Back",MENU_OPCION_NORMAL|MENU_OPCION_ESC,NULL,NULL);
		menu_add_ESC_item(array_menu_debug_tsconf_tbblue_msx);

		char titulo_ventana[33];

		//por defecto
		strcpy(titulo_ventana,"Debug TSConf");

		if (MACHINE_IS_TBBLUE)  strcpy(titulo_ventana,"Debug Next");
		if (MACHINE_IS_MSX)     strcpy(titulo_ventana,"Debug MSX");
		if (MACHINE_IS_COLECO)  strcpy(titulo_ventana,"Debug Colecovision");
		if (MACHINE_IS_SG1000)  strcpy(titulo_ventana,"Debug SG-1000");
        if (MACHINE_IS_SMS)  strcpy(titulo_ventana,"Debug Master System");
		if (MACHINE_IS_SVI)     strcpy(titulo_ventana,"Debug Spectravideo");
        if (MACHINE_IS_CPC)     strcpy(titulo_ventana,"Debug CPC");

        retorno_menu=menu_dibuja_menu_no_title_lang(&debug_tsconf_opcion_seleccionada,&item_seleccionado,array_menu_debug_tsconf_tbblue_msx,titulo_ventana);



		if ((item_seleccionado.tipo_opcion&MENU_OPCION_ESC)==0 && retorno_menu>=0) {
                        //llamamos por valor de funcion
                        if (item_seleccionado.menu_funcion!=NULL) {
                                //printf ("actuamos por funcion\n");
                                item_seleccionado.menu_funcion(item_seleccionado.valor_opcion);

                        }
                }

	} while ( (item_seleccionado.tipo_opcion&MENU_OPCION_ESC)==0 && retorno_menu!=MENU_RETORNO_ESC && !salir_todos_menus);

}


z80_byte *help_keyboard_bmp_file_mem=NULL;

//Ultima maquina activa cuando se cargo el teclado. Si cambia maquina, cargar teclado correspondiente
int help_keyboard_last_current_machine=-1;

z80_byte *help_keyboard_text_512_pcw=NULL;

void menu_help_keyboard_load_bmp(void)
{

    //Cargar el archivo bmp
    /*
    Deben ser, idealmente: 540x201.  (puede ser otro tamaño)
    bmp. 256 colour (indexed).  grabar con no-codificación run lenght,  y no sobreescribir la información de espacio de colores
    */

    char nombrebmp[PATH_MAX];


    if (MACHINE_IS_CHLOE) strcpy(nombrebmp,"keyboard_chloe.bmp");
    else if (MACHINE_IS_COLECO) strcpy(nombrebmp,"keyboard_coleco.bmp");
    else if (MACHINE_IS_CPC_464 || MACHINE_IS_CPC_4128) strcpy(nombrebmp,"keyboard_cpc_464.bmp");
    else if (MACHINE_IS_CPC_664) strcpy(nombrebmp,"keyboard_cpc_664.bmp");
    else if (MACHINE_IS_CPC_6128) strcpy(nombrebmp,"keyboard_cpc_6128.bmp");
    else if (MACHINE_IS_PCW) strcpy(nombrebmp,"keyboard_pcw_8256.bmp");
    else if (MACHINE_IS_INVES) strcpy(nombrebmp,"keyboard_inves.bmp");
    else if (MACHINE_IS_ACE) strcpy(nombrebmp,"keyboard_ace.bmp");
    else if (MACHINE_IS_MICRODIGITAL_TK90X || MACHINE_IS_MICRODIGITAL_TK90X_SPA) strcpy(nombrebmp,"keyboard_tk90x.bmp");
    else if (MACHINE_IS_MICRODIGITAL_TK95 || MACHINE_IS_MICRODIGITAL_TK95_SPA) strcpy(nombrebmp,"keyboard_tk95.bmp");
    else if (MACHINE_IS_MK14) strcpy(nombrebmp,"keyboard_mk14.bmp");
    else if (MACHINE_IS_MSX) strcpy(nombrebmp,"keyboard_msx.bmp");
    else if (MACHINE_IS_PENTAGON) strcpy(nombrebmp,"keyboard_pentagon.bmp");
    else if (MACHINE_IS_QL) strcpy(nombrebmp,"keyboard_ql.bmp");
    else if (MACHINE_IS_SAM) strcpy(nombrebmp,"keyboard_sam.bmp");
    else if (MACHINE_IS_SG1000) strcpy(nombrebmp,"keyboard_sg1000.bmp");
    else if (MACHINE_IS_SMS) strcpy(nombrebmp,"keyboard_sms.bmp");
    else if (MACHINE_IS_SVI_318) strcpy(nombrebmp,"keyboard_svi_318.bmp");
    else if (MACHINE_IS_SVI_328) strcpy(nombrebmp,"keyboard_svi_328.bmp");
    else if (MACHINE_IS_TBBLUE) strcpy(nombrebmp,"keyboard_next.bmp");
    else if (MACHINE_IS_TIMEX_TS_TC_2068) strcpy(nombrebmp,"keyboard_ts2068.bmp");
    else if (MACHINE_IS_TIMEX_TC2048) strcpy(nombrebmp,"keyboard_tc2048.bmp");
    else if (MACHINE_IS_Z88) strcpy(nombrebmp,"keyboard_z88.bmp");
    else if (MACHINE_IS_SPECTRUM_P2) strcpy(nombrebmp,"keyboard_p2.bmp");
    else if (MACHINE_IS_SPECTRUM_P2A_P3) strcpy(nombrebmp,"keyboard_p3.bmp");
    else if (MACHINE_IS_SPECTRUM_16) strcpy(nombrebmp,"keyboard_16.bmp");
    else if (MACHINE_IS_SPECTRUM_48_PLUS_SPA) strcpy(nombrebmp,"keyboard_48s.bmp");
    else if (MACHINE_IS_CZ_SPECTRUM_PLUS) strcpy(nombrebmp,"keyboard_48s.bmp"); //mismo teclado
    else if (MACHINE_IS_SPECTRUM_48_PLUS_ENG) strcpy(nombrebmp,"keyboard_48p.bmp"); //mismo teclado que el 128, aunque dejo archivos separados
    else if (MACHINE_IS_SPECTRUM_128) strcpy(nombrebmp,"keyboard_128.bmp");
    else if (MACHINE_IS_SPECTRUM_128_SPA) strcpy(nombrebmp,"keyboard_128s.bmp");
    else if (MACHINE_IS_ZX80) strcpy(nombrebmp,"keyboard_zx80.bmp");
    else if (MACHINE_IS_MICRODIGITAL_TK80) strcpy(nombrebmp,"keyboard_zx80.bmp");
    else if (MACHINE_IS_MICRODIGITAL_TK82) strcpy(nombrebmp,"keyboard_zx80.bmp");
    else if (MACHINE_IS_ZX81) strcpy(nombrebmp,"keyboard_zx81.bmp");
    else if (MACHINE_IS_TIMEX_TS1000) strcpy(nombrebmp,"keyboard_zx81.bmp"); //mismo teclado
    else if (MACHINE_IS_CZ_1000) strcpy(nombrebmp,"keyboard_zx81.bmp"); //mismo teclado
    else if (MACHINE_IS_MICRODIGITAL_TK82C) strcpy(nombrebmp,"keyboard_zx81.bmp"); //mismo teclado
    else if (MACHINE_IS_MICRODIGITAL_TK83) strcpy(nombrebmp,"keyboard_zx81.bmp"); //mismo teclado
    else if (MACHINE_IS_TIMEX_TS1500) strcpy(nombrebmp,"keyboard_ts1500.bmp");
    else if (MACHINE_IS_CZ_1500) strcpy(nombrebmp,"keyboard_ts1500.bmp"); //mismo teclado
    else if (MACHINE_IS_CZ_1000_PLUS) strcpy(nombrebmp,"keyboard_cz1000_plus.bmp");
    else if (MACHINE_IS_CZ_1500_PLUS) strcpy(nombrebmp,"keyboard_cz1500_plus.bmp");
    else if (MACHINE_IS_MICRODIGITAL_TK85) strcpy(nombrebmp,"keyboard_tk85.bmp");
    else if (MACHINE_IS_ZXEVO) strcpy(nombrebmp,"keyboard_zxevo.bmp");
    else if (MACHINE_IS_ZXUNO) strcpy(nombrebmp,"keyboard_zxuno.bmp");
    else strcpy(nombrebmp,"keyboard_48.bmp");

    //localizarlo
    char buffer_nombre[PATH_MAX];

    char nombrebmp_final[PATH_MAX];
    sprintf(nombrebmp_final,"keyboards/%s",nombrebmp);

    int existe=find_sharedfile(nombrebmp_final,buffer_nombre);
    if (!existe)  {
            debug_printf(VERBOSE_ERR,"Unable to find bmp file %s",nombrebmp_final);
            return;
    }

    //Cargamos el bmp en la paleta secundaria, para que coincida menos con otras imagenes:
    //Z88 shortcuts: paleta 0
    //Ventana about: paleta 0
    //Keyboard help: paleta 1
    help_keyboard_bmp_file_mem=util_load_bmp_file(buffer_nombre,1);


    //if (help_keyboard_bmp_file_mem==NULL) return;

    //Para el caso de pcw 8512, usamos misma imagen que 8256, pero cargamos otro bmp adicional que contiene el texto "512k"
    //que dibujamos encima del "256k"
    //No tendria sentido tener otro bmp grande solo para la imagen del teclado del 8512
    //Este bmp keyboard_pcw_8512_only_text.bmp utiliza la misma paleta que keyboard_pcw_8256.bmp,
    //asi no tengo que cargar otra paleta adicional solo para eso
    if (MACHINE_IS_PCW_8512) {
        existe=find_sharedfile("keyboards/keyboard_pcw_8512_only_text.bmp",buffer_nombre);
        if (!existe)  {
            debug_printf(VERBOSE_ERR,"Unable to find bmp file %s",nombrebmp_final);
            return;
        }
        help_keyboard_text_512_pcw=util_load_bmp_file_no_palette(buffer_nombre);
    }


}

int menu_help_keyboard_overlay_force_draw=0;

/*
z80_byte puerto_65278=255; //    db    255            ; V    C    X    Z    Sh    ;0
z80_byte puerto_65022=255; //    db    255            ; G    F    D    S    A     ;1
z80_byte puerto_64510=255; //    db              255  ; T    R    E    W    Q     ;2
z80_byte puerto_63486=255; //    db              255  ; 5    4    3    2    1     ;3
z80_byte puerto_61438=255; //    db              255  ; 6    7    8    9    0     ;4
z80_byte puerto_57342=255; //    db              255  ; Y    U    I    O    P     ;5
z80_byte puerto_49150=255; //    db              255  ; H                J         K      L    Enter ;6
z80_byte puerto_32766=255; //    db              255  ; B    N    M    Simb Space ;7
*/

//orden de teclas de derecha a izquierda:
/*
Shift z x c v
a s d f g
...
space sym m n b
*/

/*
Inicio tablas de coordenadas de teclas de cada teclado
La ubicación de cada tecla se define con 4 valores por tecla:
x1,y1,x2,y2
Donde:
x1,y1: Define la esquina superior izquierda de la tecla
x2,y2: Define la esquina inferior derecha de la tecla

Se usa valor 9999 para coordenada que queremos que sea inaccesible,
esto se usa al "saltar" un bit de puerto de teclado que no tiene tecla física asignada

Para crear nuevos teclados, activar flag keyboard_help_debug_coords,
donde mediante pulsación boton izquierdo dice la coordenada, el derecho mete salto de linea

Ver ejemplos de tablas, cada máquina tiene una o dos tablas:
1-Una tabla mínimo que genera una pulsación simple: una tecla-> cambia un bit de teclado
Van por orden, empezando por el bit menos significativo (valor 1) y luego los mas significativos (2,4,8 etc)
Y cada fila despues de los bits (columnas) de cada uno
Por ejemplo en Spectrum la tabla empieza con:
-Tecla shift
-Tecla z
-Tecla x
-Tecla c
-Tecla v
-Tecla a
-Tecla s
etc...

2-Tabla adicional para las teclas "dobles": una tecla-> cambia dos bits de teclado (ejemplo tecla " del spectrum es symbol+p)
Esta tabla define en cada item los puertos que modifica, aqui no hay orden

También hay que gestionar los casos de teclados con mas de 5 columnas o mas de 8 filas. Ver ejemplos de maquinas no-Spectrum
*/



//para keyboard_48.bmp
//4 valores de cada tecla: x,y inicial, x,y final
int keyboard_map_table_coords_48[40*4]={
8,166,54,190,       72,166,106,190,     120,166,158,190,    172,166,206,190,    220,166,256,190,
46,116,80,140,      94,116,130,142,     146,116,180,140,    196,116,232,142,    245,116,280,140,
34,68,68,92,        84,68,120,92,       132,68,168,92,      184,68,220,92,      235,68,266,92,
8,20,44,44,         58,20,96,44,        110,20,144,44,      160,20,194,44,      210,20,244,44,
458,20,492,44,      408,20,444,44,      358,20,394,44,      308,20,342,44,      260,20,294,44,
484,68,518,92,      434,68,468,92,      384,68,418,92,      334,68,370,92,      284,68,316,92,
494,116,530,142,    446,118,480,142,    396,118,428,142,    346,118,378,142,    296,118,328,142,
468,166,530,190,    420,166,454,190,    370,166,404,190,    321,166,354,190,    270,166,304,190,
};

//modelos +
int keyboard_map_table_coords_48p[40*4]={
3,122,90,160,130,124,170,162,170,124,210,160,210,124,250,160,252,124,290,160,
108,82,150,120,150,84,190,120,192,84,230,120,230,84,270,120,272,84,310,120,
100,42,140,80,140,44,180,82,180,44,220,80,222,44,260,78,258,44,300,78,
80,2,120,40,120,2,160,42,160,2,200,40,200,2,240,40,242,2,280,38,
440,2,480,38,400,2,438,38,360,2,400,38,322,2,358,38,280,2,320,38,
460,44,498,80,420,44,458,80,380,42,416,80,340,44,380,80,302,42,340,80,
472,82,537,120,432,84,468,120,392,82,430,120,352,82,388,120,312,84,350,120,
200,164,378,198,4,162,40,198,370,124,408,158,330,124,368,160,290,124,330,162,
};

int keyboard_map_table_coords_p2[40*4]={
2,124,88,160,134,124,170,160,174,124,210,158,210,122,252,158,252,124,288,156,
112,84,150,120,152,82,186,120,192,84,230,120,232,82,270,118,272,82,308,114,
102,44,138,80,140,42,182,80,182,42,220,78,220,44,262,78,260,42,300,80,
82,2,120,40,122,2,162,40,162,2,200,38,202,2,240,40,242,2,280,40,
442,2,480,40,402,2,438,40,362,2,400,40,322,2,360,40,282,2,318,38,
462,44,498,78,420,44,458,78,382,42,420,78,340,44,378,78,300,44,340,78,
472,82,536,118,430,84,468,118,392,84,428,118,350,84,390,120,312,84,348,120,
200,164,378,198,4,164,38,198,372,124,408,158,332,124,368,158,292,124,330,160,
};



int keyboard_map_table_coords_zxuno[40*4]={
6,124,34,148,96,124,116,148,134,124,154,150,176,124,198,150,214,124,236,148,
76,84,96,112,114,84,132,110,152,84,176,112,192,84,216,110,232,82,254,110,
62,42,84,70,100,46,124,72,144,44,162,70,184,46,206,70,224,46,246,70,
44,4,64,28, 84,6,104,30, 122,6,144,30, 164,4,188,28, 202,4,224,26,
406,2,430,32,366,2,386,30,324,4,346,32,284,4,306,30,244,4,268,28,
424,46,446,72,386,44,404,70,346,46,366,72,304,42,326,70,262,42,286,70,
550,46,590,114,394,86,418,112,356,84,380,116,318,84,338,110,274,84,296,108,
166,162,380,192,6,164,44,190,336,124,358,152,296,124,316,152,256,126,276,150,

};


int keyboard_map_table_coords_zxevo[40*4]={
6,118,35,143,86,119,106,143,121,120,140,144,156,121,176,142,193,120,214,144,
67,83,86,106,104,83,123,106,139,83,159,105,174,82,196,106,211,84,234,109,
57,46,78,69,93,46,114,69,129,46,150,70,166,46,187,68,203,47,224,71,
40,10,59,34,75,11,96,34,111,10,132,35,146,10,167,33,182,10,205,33,
367,9,388,35,330,8,350,31,294,9,316,34,256,9,279,32,219,9,240,31,
386,45,407,70,349,46,369,70,313,47,332,68,274,46,296,70,239,46,260,71,
497,45,538,107,357,83,379,106,321,83,342,107,285,84,306,107,249,83,269,107,
148,156,346,177,450,118,535,144,303,119,325,144,267,119,287,142,230,118,252,143,

};


int keyboard_map_table_coords_chloe[40*4]={
2,122,40,154,82,122,114,152,118,122,150,152,156,124,186,156,190,124,222,156,
64,82,94,114,100,82,132,114,136,82,166,114,172,84,204,114,208,84,238,114,
56,42,86,74,94,42,122,76,128,42,158,74,164,44,192,72,200,44,230,72,
38,4,70,34,74,2,104,38,110,4,140,36,146,4,176,36,182,4,210,34,
362,4,392,36,326,4,354,34,290,4,318,34,254,4,284,34,218,4,246,32,
380,44,408,74,344,44,374,76,308,44,338,76,272,42,304,76,236,44,270,76,
490,44,534,116,354,84,380,112,318,84,346,116,280,82,312,114,244,82,274,114,
136,164,354,194,92,164,130,194,298,122,328,154,264,124,290,156,228,124,256,154,

};

int keyboard_map_table_coords_tk85[40*4]={
8,166,52,188, 72,162,104,188, 122,164,156,190, 172,164,204,188, 224,162,254,188,
46,116,80,138,96,114,128,140,146,112,180,140,196,112,232,140,246,112,282,138,
32,64,64,88,84,66,114,86,134,66,166,88,182,62,218,88,234,66,268,88,
8,14,40,40,58,14,90,38,108,12,140,38,158,14,192,38,210,12,242,36,
462,14,492,36,410,14,444,36,360,12,392,40,312,12,344,38,260,12,296,36,
486,64,518,86,434,64,468,86,384,62,418,86,336,62,368,86,284,62,318,92,
498,112,530,136,446,112,482,138,398,114,432,138,348,112,382,136,298,112,330,140,
472,164,532,190,422,164,452,188,372,162,402,186,324,164,354,190,272,164,304,188,
};



int keyboard_map_table_coords_tk90x[40*4]={
22,166,66,190,82,166,110,188,130,168,162,188,178,166,208,190,226,166,256,188,
58,118,90,140,108,120,134,138,154,120,184,140,202,120,234,140,248,120,280,138,
48,72,78,94,96,70,126,94,142,72,170,92,188,70,218,92,236,70,270,92,
22,24,54,46,72,24,102,46,118,22,148,46,164,22,196,46,214,24,242,48,
450,22,482,46,402,24,434,46,356,24,386,46,308,24,336,46,260,22,290,46,
474,70,504,96,426,72,458,94,378,70,410,96,332,72,362,96,282,70,314,92,
488,120,516,140,438,120,468,142,392,120,420,142,344,120,374,144,296,118,328,142,
464,168,516,190,416,168,446,188,368,168,398,188,320,168,352,188,272,166,304,188,
};

int keyboard_map_table_coords_tk95[40*4]={
2,134,44,154,90,132,114,156,124,132,150,154,162,132,188,156,200,134,226,154,
72,96,100,122,106,98,134,122,144,98,170,120,178,98,206,118,214,98,240,120,
62,58,90,84,100,60,126,86,136,62,160,82,168,62,194,82,206,62,232,82,
44,24,70,48,82,26,104,48,120,26,142,44,152,24,180,48,188,24,214,48,
368,26,394,50,332,24,358,48,296,26,320,48,260,26,286,48,224,24,250,48,
386,62,412,82,352,60,378,82,316,62,342,84,278,62,304,86,244,64,268,84,
490,60,536,80,364,98,386,120,326,98,350,122,290,98,314,118,254,98,278,118,
106,168,420,188,450,132,536,154,306,134,330,154,272,134,294,156,238,136,258,156,
};

/*
z80_byte puerto_65278=255; //    db    255            ; V    C    X    Z    Sh    ;0
z80_byte puerto_65022=255; //    db    255            ; G    F    D    S    A     ;1
z80_byte puerto_64510=255; //    db              255  ; T    R    E    W    Q     ;2
z80_byte puerto_63486=255; //    db              255  ; 5    4    3    2    1     ;3
z80_byte puerto_61438=255; //    db              255  ; 6    7    8    9    0     ;4
z80_byte puerto_57342=255; //    db              255  ; Y    U    I    O    P     ;5
z80_byte puerto_49150=255; //    db              255  ; H                J         K      L    Enter ;6
z80_byte puerto_32766=255; //    db              255  ; B    N    M    Simb Space ;7
*/
int keyboard_map_table_coords_next[40*4]={
    2,124,88,161, 143,124,181,159, 183,124,220,161, 224,124,260,159, 264,123,300,161,
    109,83,149,119,152,83,189,119,193,84,230,120,234,83,270,120,274,84,310,120,
    101,42,136,79,142,42,178,79,182,42,220,79,226,43,260,78,264,43,299,78,
    80,2,115,38,122,3,156,38,162,3,197,36,203,2,240,39,243,2,281,38,
    445,4,480,39,406,3,442,38,365,2,401,38,324,2,361,39,285,2,320,36,
    465,42,500,80,426,43,461,79,385,42,422,80,345,42,382,79,304,43,341,78,
    475,84,538,118,436,84,472,119,396,83,432,119,356,84,391,119,314,84,353,119,
    203,164,381,198,2,165,38,198,384,124,421,159,344,125,381,159,304,124,339,159,
};

int keyboard_map_table_coords_cz1000_plus[40*4]={
    6,144,54,170,74,144,112,168,128,146,164,170,182,148,220,168,234,148,270,170,
    48,102,84,124,100,102,136,122,156,102,188,124,210,104,244,124,262,104,300,124,
    32,54,70,76,90,56,124,78,144,54,180,78,196,56,230,78,250,52,286,78,
    10,10,46,32,64,10,98,32,118,12,152,34,170,10,208,34,226,12,260,34,

    490,10,526,36,438,12,472,36,382,12,418,34,330,12,366,32,276,10,314,32,
    518,56,554,80,464,58,498,78,412,58,448,80,358,56,394,78,306,56,342,78,
    534,102,568,122, 478,100,514,126, 424,102,460,126, 370,104,408,122,318,102,352,124,
    506,148,568,170,450,146,488,170,398,148,432,168,344,148,376,170,290,150,326,172,


};

int keyboard_map_table_coords_cz1500_plus[40*4]={
    12,184,60,214,74,184,112,212,126,184,160,210,178,182,214,212,232,186,266,212,
    52,126,86,156,102,128,134,154,150,126,186,156,204,126,240,154,258,128,292,156,
    36,70,72,98,88,70,124,100,140,72,176,98,190,68,226,96,242,68,278,96,
    14,14,48,42,64,14,98,42,114,14,150,40,164,12,202,40,216,14,252,40,

    478,12,514,40,426,14,458,40,374,12,408,38,320,12,356,40,270,14,304,40,
    506,72,540,96,452,70,488,96,398,70,436,98,348,68,384,98,294,70,332,96,
    520,128,556,156,464,126,504,156,412,128,450,154,360,128,398,154,308,126,348,158,
    494,188,560,214,442,186,476,214,386,186,428,214,334,184,372,214,282,184,320,210,

};

int keyboard_map_table_coords_zx8081[40*4]={
30,162, 68,186, 78,160, 116,186, 126,162, 162,186, 174,162, 212,186, 222,162, 260,186,
52,114, 92,142, 98,114, 140,142, 148,112, 188,140, 198,116, 236,142, 244,116, 284,142,
42,68, 80,96, 90,68, 128,96, 138,68, 178,96, 186,70, 224,96, 232,68, 272,96,
16,22, 56,48, 66,22, 104,50, 114,22, 152,48, 160,24, 202,50, 210,24, 248,50,
448,22, 486,48, 400,22, 438,48, 354,24, 390,50, 306,22, 344,48, 256,24, 296,50,
472,70, 512,96, 426,70, 464,94, 378,68, 414,94, 328,68, 368,94, 280,68, 318,92,
486,114, 524,140, 436,116, 474,140, 388,114, 428,142, 340,114, 380,142, 292,114, 334,142,
460,160, 500,188, 412,160, 452,188, 364,162, 402,186, 316,160, 354,186, 270,162, 306,186,
};

int keyboard_map_table_coords_ace[40*4]={
34,158,62,184,82,158,110,186,128,154,160,184,178,158,206,184,224,156,252,184,
56,110,84,136,104,110,134,136,152,110,182,136,198,108,228,136,244,108,276,136,
40,60,72,90,90,62,120,88,138,62,168,88,184,62,216,90,234,60,264,88,
18,14,50,42, 66,14,98,40, 118,14,146,40, 162,12,194,40, 210,14,242,42,
448,12,482,40,402,12,434,40,356,16,382,40,308,16,336,40,260,16,288,40,
474,62,504,86,426,62,454,88,378,64,406,88,330,64,360,88,282,64,312,88,
486,110,516,136,438,112,468,136,388,110,418,136,342,110,370,136,296,110,322,136,
464,158,514,184, 416,158,446,184, 368,158,398,184, 320,156,350,184, 272,156,302,184,
};

//4 de direccion, 2 botones
int keyboard_map_table_coords_sms[6*4]={
66,92,86,110, 28,92,48,110, 44,110,68,132, 44,72,68,94, 130,100,154,122,

164,100,188,122,

};

int keyboard_map_table_coords_sg1000[6*4]={
54,70,76,88, 12,70,38,88, 34,94,56,110, 40,48,58,66, 14,46,32,68,
66,46,80,68,

};

int keyboard_map_table_coords_coleco[6*4]={
42,12,58,28, 4,12,20,28, 24,32,42,44, 24,2,42,10, 56,38,74,52,
50,122,64,134

};

int keyboard_map_table_coords_msx[8*9*4]={
290,34,310,54, 28,32,48,52, 58,34,78,54, 84,34,104,52, 114,34,134,52, 144,34,164,54, 172,34,194,54, 202,34,222,52,
230,34,248,52, 262,34,280,54, 318,34,340,52, 348,32,372,54, 376,34,398,54, 332,62,354,82, 362,62,384,84, 310,90,332,110,
340,90,362,110, 372,90,392,110, 266,122,290,142, 296,122,320,140, 324,120,348,140, 406,120,430,138, 48,90,70,112, 178,120,202,142,
120,120,144,142,106,90,126,110,98,62,118,84,134,92,154,110,166,90,186,114,194,90,216,114,244,62,268,84,222,90,244,110,
252,92,274,112,282,92,308,114,238,120,258,142,208,120,230,140,274,62,296,80,304,62,326,80,42,62,64,84,128,62,154,86,
76,92,96,112, 156,62,182,84, 214,60,236,84, 148,120,170,140, 68,62,92,82, 92,120,110,138, 188,62,206,82, 62,120,82,142,
4,120,54,142,4,92,40,110,92,150,112,170,64,150,84,168,358,148,380,168,2,6,34,26,44,6,78,24,84,6,122,24,
130,6,162,24,174,6,208,26,4,34,20,54,4,62,34,82,260,6,296,28,404,32,424,50,216,6,252,24,396,62,430,110,
120,150,350,168, 452,6,474,24, 480,4,500,26, 508,4,530,26, 452,82,474,116, 476,60,510,76, 476,118,514,136, 512,82,534,114,

};

int keyboard_map_table_coords_svi_318[8*11*4]={
292,30,312,44,38,30,58,44, 68,30,88,44,96,30,116,44,124,30,146,44,154,30,174,42,180,30,202,44,208,30,230,44,
238,30,256,44,264,30,282,44,314,86,332,98,340,86,360,98,274,112,292,126,346,28,366,44,300,112,318,126,326,112,346,124,
318,30,338,44,62,86,80,100,190,112,208,126,134,114,152,128,120,86,140,100,112,58,134,72,148,84,168,100,176,86,194,98,
204,86,222,98,252,58,270,70,230,84,250,100,258,86,276,98,284,84,304,100,244,112,264,126,218,114,236,126,280,58,298,70,
304,56,326,70,54,58,74,72,140,60,160,72,92,88,110,100,168,58,186,72,224,56,244,72,164,116,182,124,82,58,104,72,
104,114,124,126, 196,58,214,70, 78,116,98,128, 332,56,352,70, 388,58,408,72, 360,56,378,72, 374,30,408,42, 462,58,488,82,
32,114,68,128,34,88,54,100,76,140,94,156,106,142,122,156,10,30,30,44,282,4,312,16,368,84,408,96,440,82,464,100,
10,4,40,14,56,2,90,16,110,4,140,14,160,4,188,14,206,2,232,14,332,4,352,16,360,2,380,16,462,104,484,124,
134,140,290,154,10,58,46,74,388,2,408,16,6,88,26,100,426,30,444,44, 9999,9999,9999,9999, 9999,9999,9999,9999, 488,84,506,106,
9999,9999,9999,9999, 9999,9999,9999,9999, 9999,9999,9999,9999, 9999,9999,9999,9999, 9999,9999,9999,9999, 9999,9999,9999,9999, 9999,9999,9999,9999, 9999,9999,9999,9999,
9999,9999,9999,9999, 9999,9999,9999,9999, 9999,9999,9999,9999, 9999,9999,9999,9999, 9999,9999,9999,9999, 9999,9999,9999,9999, 9999,9999,9999,9999, 9999,9999,9999,9999,

};

int keyboard_map_table_coords_svi_328[8*11*4]={
294,34,312,52,38,32,56,52,66,32,84,52,94,32,114,54,124,32,140,52,150,32,170,52,178,32,198,50,206,34,226,52,
236,32,256,52,266,32,284,50,318,90,334,106,342,90,362,108,272,118,290,134,352,32,370,52,302,116,320,136,328,118,346,134,
322,32,342,50,60,90,78,108,188,118,206,136,132,118,150,138,118,88,136,106,110,62,126,78,144,90,162,106,172,90,192,108,
202,90,222,108,252,64,272,80,230,90,248,108,256,90,276,108,288,90,304,108,244,118,262,134,216,118,232,134,280,62,296,78,
310,62,326,78,52,62,70,82,138,62,158,80,88,90,106,108,166,60,184,78,226,62,240,78,160,118,178,134,80,60,102,80,
104,118,124,136,196,60,216,80,74,120,92,134,336,60,356,80,396,62,412,80,364,62,384,78,380,32,414,50,464,6,480,22,
32,120,66,136,30,88,52,110,76,146,92,164,104,144,122,162,8,32,26,50,272,2,292,24,374,90,412,106,434,34,452,50,
2,4,40,24,52,4,92,24,102,4,140,24,154,6,190,22,202,6,240,22,322,2,342,24,352,4,368,22,464,32,480,48,
134,146,346,162,4,62,44,80,382,4,398,22,4,106,20,122,434,4,452,22,490,6,510,24, 9999,9999,9999,9999, 492,32,510,52,
434,146,450,164,432,118,450,136,462,118,480,134,490,116,508,136,434,90,452,108,462,90,478,106,490,90,510,108,434,62,450,80,
462,60,480,78,492,60,510,80,520,64,538,80,522,92,538,110,520,36,538,50,524,6,538,22,488,144,506,162,462,146,478,164,

};

/*
z80_byte puerto_65278=255; //    db    255            ; V    C    X    Z    Sh    ;0
z80_byte puerto_65022=255; //    db    255            ; G    F    D    S    A     ;1
z80_byte puerto_64510=255; //    db              255  ; T    R    E    W    Q     ;2
z80_byte puerto_63486=255; //    db              255  ; 5    4    3    2    1     ;3
z80_byte puerto_61438=255; //    db              255  ; 6    7    8    9    0     ;4
z80_byte puerto_57342=255; //    db              255  ; Y    U    I    O    P     ;5
z80_byte puerto_49150=255; //    db              255  ; H                J         K      L    Enter ;6
z80_byte puerto_32766=255; //    db              255  ; B    N    M    Simb Space ;7
*/
int keyboard_map_table_coords_sam[40*4]={
5,115,66,139, 83,115,105,139, 119,116,143,138, 156,114,177,139, 193,116,216,140,
65,80,86,101, 102,78,122,102, 138,77,158,103, 174,79,196,102, 212,79,234,102,
58,42,77,66, 92,40,113,64, 130,41,149,65, 166,41,187,66, 204,42,224,66,
38,4,59,28,73,5,93,26,110,5,131,28,147,4,167,29,182,4,206,28,

375,2,396,27, 336,2,357,27, 297,2,319,28, 259,3,281,27, 221,2,243,27,
391,41,415,65, 354,40,378,65, 316,40,338,65, 279,41,301,64, 239,39,262,65,
477,79,529,104, 363,78,386,103, 325,77,346,102, 288,77,312,105, 249,78,271,101,
120,152,405,177, 4,151,48,175, 306,116,330,141, 269,116,292,140, 230,116,255,140,



};


/*Tablas teclado
Bit:
Line	7       6       5	    4	    3	    2	    1	    0
----------------------------------------------------------------------
&40	    FDot	ENTER	F3	    F6	    F9	    CURDOWN	CURRIGHT CURUP
&41	    F0	    F2	    F1	    F5	    F8	    F7	    COPY	CURLEFT
&42	    CONTROL	\	    SHIFT	F4	    ]	    RETURN	[	    CLR
&43	    .   	/	     :	    ;	    P	    @	    -	    ^
&44	    ,	    M	    K	    L	    I	    O	    9	    0
&45	    SPACE	N	    J	    H	    Y	    U	    7	    8
&46	    V	    B	    F	    G (Joy2 fire)	T (Joy2 right)	R (Joy2 left)	5 (Joy2 down)	6 (Joy 2 up)
&47	    X	    C	    D	    S	    W	    E	    3	    4
&48	    Z	    CAPSLOCK A	    TAB	    Q	    ESC	    2	    1
&49	    DEL	    Joy 1   Fire 3 (CPC only)	     Joy 1 Fire 2	   Joy1 Fire 1	   Joy1 right	  Joy1 left	   Joy1 down	Joy1 up
*/
int keyboard_map_table_coords_cpc_6128[8*10*4]={
574,180,595,201,610,215,632,236,574,215,597,235,608,77,629,99,607,112,630,134,608,146,631,166, 432,215,527,236, 609,179,630,200,

538,214,562,236, 84,214,133,236, 537,78,559,100, 572,77,595,99, 572,111,594,133, 536,145,560,167, 574,146,596,167, 539,181,560,201,
466,77,487,101, 449,112,470,134, 486,113,524,168, 458,146,481,168, 537,111,559,133, 7,179,72,202 ,440,180,463,204, 5,214,70,236,

430,78,452,99,395,78,417,99,414,113,436,136,377,112,400,134,423,146,444,167,386,146,409,168,406,181,427,202,369,181,391,201,
360,78,384,101,323,77,347,101,343,112,365,134,306,112,329,134,352,146,374,169,316,147,338,168,299,182,319,203,332,180,357,204,

288,78,311,99,252,77,275,100,270,112,293,133,235,112,258,134,244,146,265,167,280,146,303,167,262,181,284,202,147,214,419,238,
218,77,240,99,182,77,205,101,164,113,187,135,200,112,222,133,207,146,230,167,173,146,196,168,226,181,248,202,190,181,212,204,

147,77,168,98,111,77,135,99,129,111,152,134,94,112,116,133,103,146,124,168,139,146,162,168,156,180,178,201,119,180,142,201,
41,77,63,100, 78,77,99,100, 6,76,28,100, 58,112,81,132, 7,111,45,134, 67,145,90,167, 5,145,53,169, 86,180,109,201,

9999,9999,9999,9999, 9999,9999,9999,9999, 9999,9999,9999,9999, 9999,9999,9999,9999, 9999,9999,9999,9999, 9999,9999,9999,9999, 9999,9999,9999,9999, 502,77,524,100

};


int keyboard_map_table_coords_cpc_664[8*10*4]={
578,3,602,24, 612,35,635,57, 578,69,600,89, 613,98,633,117, 612,132,632,151, 613,165,633,183, 608,193,630,215, 578,197,598,214,
544,35,566,57, 578,35,601,57, 546,98,567,117, 578,98,601,117, 579,132,600,151, 546,163,567,182, 580,164,599,181, 546,195,566,214,

431,65,452,88, 413,99,433,120, 448,98,500,152, 420,132,440,151, 546,133,566,151, 8,162,71,183, 403,164,423,185, 405,196,427,215,
398,66,416,87, 364,71,385,90, 379,98,401,120, 347,99,366,120, 387,133,408,152, 354,132,375,152, 371,166,389,184, 338,165,355,182,

333,68,351,89, 299,68,319,89, 313,100,333,120, 280,100,301,119, 321,133,342,151, 289,133,308,152, 272,165,292,183, 303,165,322,184,
266,69,285,89, 233,69,252,89, 249,101,267,120, 215,100,233,120, 221,133,242,151, 256,132,276,151, 237,164,259,184, 110,196,389,219,
200,68,220,88, 167,69,186,90, 151,100,171,120, 181,100,202,120, 189,133,209,151, 159,132,179,151, 206,167,226,183, 175,164,194,181,
133,69,155,90, 102,68,122,88, 120,99,139,120, 87,100,108,120,   94,132,115,149, 127,132,146,150, 142,164,162,183, 111,165,131,183,
37,68,56,89,   68,69,89,89,   6,69,29,90,     56,101,75,120,    8,103,43,123,    63,133,83,151,   8,131,52,151,     80,164,99,182,

9999,9999,9999,9999, 9999,9999,9999,9999, 9999,9999,9999,9999, 9999,9999,9999,9999, 9999,9999,9999,9999, 9999,9999,9999,9999, 9999,9999,9999,9999, 463,68,500,88

};


int keyboard_map_table_coords_cpc_464[8*10*4]={
578,9,602,31, 612,42,635,64, 578,76,600,96, 613,120,633,139, 612,154,632,173,613,187,633,205, 608,215,630,237, 578,219,598,236,
544,41,566,65, 578,41,601,63, 546,120,567,139,578,120,601,140,579,154,600,173,546,185,567,204,580,186,599,203,546,217,566,236,
431,85,452,108, 413,119,433,138, 448,118,500,172, 420,152,440,171, 546,153,566,171, 8,182,71,203, 403,184,423,205, 400,216,422,235,
398,86,416,107, 364,86,385,105, 379,118,401,137, 347,119,366,138, 387,153,408,172, 354,152,375,172, 371,186,389,204, 338,185,355,202,
331,86,351,106,298,87,316,105,313,120,333,138, 280,120,301,139,321,153,342,171,289,153,308,172,272,185,292,203,303,185,322,204,
265,87,285,105,232,87,250,105,249,121,267,138,215,120,233,138,221,153,242,171,256,152,276,171,237,184,259,204,110,216,389,239,
200,87,217,105, 166,86,186,105, 151,120,171,138, 181,120,202,138, 189,153,209,171, 159,152,179,171, 206,187,226,203, 175,184,194,201,
134,85,156,106, 103,87,123,104, 120,119,139,137, 87,120,108,137, 94,152,115,169, 127,152,146,170, 142,184,162,203, 111,185,131,203,
39,87,58,105,70,87,91,105,6,86,29,107,56,121,75,137,8,118,43,137,63,153,83,171,8,151,52,171,80,184,99,202,
9999,9999,9999,9999, 9999,9999,9999,9999, 9999,9999,9999,9999, 9999,9999,9999,9999, 9999,9999,9999,9999, 9999,9999,9999,9999, 9999,9999,9999,9999, 463,86,500,106

};



int keyboard_map_table_coords_pcw[8*11*4]={
503,164,524,189, 536,200,560,222, 503,199,523,223, 605,62,627,81, 605,92,627,116, 605,128,625,151, 606,163,625,186, 571,164,594,187,

470,202,489,224,434,201,457,226,538,62,560,83,573,63,594,83,572,95,592,116,538,130,559,152,573,129,591,151,536,164,558,186,
436,61,456,84,419,96,440,121,458,98,490,154,427,131,448,152,538,95,559,118,3,166,59,189,410,166,428,187,96,200,118,222,

402,63,423,81,369,62,389,83,385,97,405,118,351,97,372,119,394,131,412,152,360,132,380,154,375,167,398,190,341,167,362,190,
335,63,355,86,300,62,321,83,318,97,338,121,283,98,304,120,326,132,345,154,291,131,312,155,272,166,291,191,308,168,329,190,

267,63,286,84,234,63,253,86,251,97,270,119,217,99,236,119,224,133,244,154,257,132,278,154,240,168,259,188,129,202,387,226,
201,62,221,85,167,62,186,85,150,98,170,120,183,98,204,119,190,133,210,155,156,131,177,156,206,168,227,192,171,166,193,187,

135,63,154,85,101,62,121,86,118,98,138,118,84,99,103,117,92,132,111,154,124,132,143,156,  139,167,157,188, 106,167,125,190,
36,63,55,86,69,63,89,86,4,63,23,85,51,97,72,119,3,97,35,117,58,132,79,155,3,132,43,154,74,167,94,189,
9999,9999,9999,9999, 9999,9999,9999,9999, 9999,9999,9999,9999, 9999,9999,9999,9999, 9999,9999,9999,9999, 9999,9999,9999,9999, 9999,9999,9999,9999, 470,62,490,83,
502,128,522,151,47,200,84,223,504,61,523,82,400,202,419,226,505,94,523,116,605,200,626,222,570,199,592,221,2,200,34,223,
};


int keyboard_map_table_coords_timex_computer[40*4]={
20,134,64,158,78,132,110,156,122,132,154,160,166,132,200,160,210,132,242,160,
52,94,86,118,100,96,130,118,142,94,176,118,188,94,222,118,234,96,266,118,
42,56,74,78,88,56,120,78,134,56,166,80,180,54,210,78,222,56,252,78,
20,16,50,40,64,16,98,40,110,18,142,40,154,16,186,38,200,16,232,40,
424,18,458,42,378,16,412,38,334,16,368,40,290,16,322,42,246,16,276,40,
448,54,482,78,402,54,436,80,358,54,390,80,312,54,346,80,268,56,302,80,
458,94,526,118,412,96,446,118,368,94,400,118,324,94,356,118,278,96,312,118,
120,172,424,196, 392,134,424,156,346,132,378,154,302,134,334,156,256,132,290,156,
};



int keyboard_map_table_coords_timex_sinclair_1500[40*4]={
8,162,54,188,70,162,106,188,120,160,156,188,170,162,206,186,220,162,258,188,
44,112,80,140,96,112,132,138,146,112,180,136,196,114,230,136,246,112,280,136,
32,62,70,88,84,62,118,86,132,60,168,88,184,60,218,86,232,62,268,88,
6,12,44,38,58,12,92,36,108,12,144,36,156,10,192,36,208,10,244,38,
458,10,496,38,408,10,446,38,358,10,394,38,310,12,342,36,258,10,296,38,
484,60,518,88, 434,60,468,88, 384,60,420,86, 334,62,368,88, 282,60,320,86,
498,112,534,140,448,110,482,136,398,110,430,134,346,110,384,138,294,110,330,134,
472,164,532,188,424,162,458,186,370,162,406,186,322,162,356,186,272,162,304,186,
};


int keyboard_map_table_coords_pentagon[40*4]={
72,142,108,178, 112,142,146,174, 154,142,186,174, 194,144,224,178, 232,142,266,180,
82,100,118,136,122,102,158,138,162,102,196,138,200,104,236,138,242,104,278,138,
72,62,106,98,112,64,148,98,152,64,188,100,192,64,228,96,230,64,268,98,
60,22,98,58,102,22,138,60,142,24,180,60,182,22,216,58,222,22,258,58,
420,24,456,56,380,24,418,58,340,22,374,58,302,22,336,56,260,20,296,56,
432,62,464,96,388,62,428,98,352,64,386,98,310,64,348,98,270,64,306,98,
442,106,478,138,400,102,438,138,362,102,396,138,320,102,356,138,280,102,316,138,
430,144,468,180,392,142,428,180,352,142,390,180,312,142,348,180,272,142,306,178,
};

//8 filas, 4 columnas, 4 valores por cada tecla
            /*
            mk14_keystatus

               Matriz teclado
               128 64  32  16

                0  8   -   A	mk14_keystatus[0]
            	1  9   -   B	mk14_keystatus[1]
            	2  -   GO  C	mk14_keystatus[2]
            	3  -   MEM D	mk14_keystatus[3]
            	4  -   ABR -	mk14_keystatus[4]
            	5  -   -   -	mk14_keystatus[5]
            	6  -   -   E	mk14_keystatus[6]
            	7  -   TER F	mk14_keystatus[7]

            GO: mapeado a G
            MEM: mapeado a M
            ABR: mapeado a Z
            TERM: mapeado a T

            //El MK14 usa los 4 bits superiores de cada mk14_keystatus para almacenar el estado de las teclas
            //pero yo lo almaceno en los 4 bits inferiores porque me facilita la vida tenerlo ahi en la ventana de Keyboard Help
            */
//Los 9999 son teclas no mapeadas, al ser coordenadas altas, no son usables
int keyboard_map_table_coords_mk14[8*4*4]={
116,28,134,42, 9999,9999,9999,9999, 48,56,68,72,50,138,68,154,
114,54,136,70, 9999,9999,9999,9999, 82,56,102,72, 16,110,36,126,
114,82,136,98, 16,30,40,46, 9999,9999,9999,9999, 50,108,70,124,
116,108,136,124, 50,28,70,44, 9999,9999,9999,9999, 82,108,100,124,
9999,9999,9999,9999, 82,28,102,44, 9999,9999,9999,9999, 16,84,36,98,
9999,9999,9999,9999, 9999,9999,9999,9999, 9999,9999,9999,9999, 48,82,70,98,
116,138,136,156, 9999,9999,9999,9999, 9999,9999,9999,9999, 82,80,104,100,
82,138,100,152, 16,138,34,156, 9999,9999,9999,9999, 16,56,36,74,

};

//Estructura para teclas que genera doble pulsacion, como "extended mode" (shift+symbol)
//Adicionalmente tambien se usa para teclas repetidas, como symbol en un spectrum+. en ese caso puerto2 vale NULL;
struct s_keyboard_help_double_key {
    int x1,y1,x2,y2; //coordenadas de la tecla

    z80_byte *puerto1;
    z80_byte mascara1;

    z80_byte *puerto2;
    z80_byte mascara2;
};

typedef struct s_keyboard_help_double_key keyboard_help_double_key;

//tecla extend mode spectrum+
//82,2, 120,60
//tecla delete
//2,44, 58,80

#define KEY_PORT_VALUE_SHIFT &puerto_65278,1
#define KEY_PORT_VALUE_SYMBOL &puerto_32766,2

/*
z80_byte puerto_65278=255; //    db    255            ; V    C    X    Z    Sh    ;0
z80_byte puerto_65022=255; //    db    255            ; G    F    D    S    A     ;1
z80_byte puerto_64510=255; //    db              255  ; T    R    E    W    Q     ;2
z80_byte puerto_63486=255; //    db              255  ; 5    4    3    2    1     ;3
z80_byte puerto_61438=255; //    db              255  ; 6    7    8    9    0     ;4
z80_byte puerto_57342=255; //    db              255  ; Y    U    I    O    P     ;5
z80_byte puerto_49150=255; //    db              255  ; H                J         K      L    Enter ;6
z80_byte puerto_32766=255; //    db              255  ; B    N    M    Simb Space ;7
*/

//Teclas adicionales spectrum+
keyboard_help_double_key keyboard_map_additional_48p[]={
    { 500,162, 537,198,     KEY_PORT_VALUE_SYMBOL,  NULL, 0},  //symbol derecha
    { 452,124,536,158,      KEY_PORT_VALUE_SHIFT,   NULL, 0}, //caps shift derecha
    { 504,42,537,119,       &puerto_49150,1,   NULL, 0}, //trozo del enter parte de arriba
    { 2,82, 60, 120,        KEY_PORT_VALUE_SHIFT,   KEY_PORT_VALUE_SYMBOL }, //extend mode
    { 2,44, 58,80,          &puerto_65278,1,        &puerto_61438,1 },  //delete
    { 2,2,38,36,            &puerto_63486,4,        KEY_PORT_VALUE_SHIFT }, //true video
    { 44,2,76,36,           &puerto_63486,8,        KEY_PORT_VALUE_SHIFT }, //inverse video
    { 62,44,98,80,          &puerto_61438,2,        KEY_PORT_VALUE_SHIFT },//graph
    { 60,82,108,118,        &puerto_63486,1,        KEY_PORT_VALUE_SHIFT },//edit
    { 90,124,128,158,       &puerto_63486,2,        KEY_PORT_VALUE_SHIFT }, //caps lock
    { 42,164,78,198,        &puerto_57342,2,        KEY_PORT_VALUE_SYMBOL }, // ;
    { 82,164,116,198,       &puerto_57342,1,        KEY_PORT_VALUE_SYMBOL }, // "
    { 122,164,158,198,      &puerto_63486,16,       KEY_PORT_VALUE_SHIFT }, // flecha izq
    { 162,164,196,198,      &puerto_61438,4,        KEY_PORT_VALUE_SHIFT }, // flecha der
    { 382,164,418,198,      &puerto_61438,8,        KEY_PORT_VALUE_SHIFT }, // flecha arr
    { 422,164,458,198,      &puerto_61438,16,       KEY_PORT_VALUE_SHIFT }, // flecha abaj
    { 460,162,496,198,      &puerto_32766,8,        KEY_PORT_VALUE_SYMBOL }, // ,
    { 412,124,448,160,      &puerto_32766,4,        KEY_PORT_VALUE_SYMBOL }, // .
    { 482,2,538,38,         &puerto_32766,1,        KEY_PORT_VALUE_SHIFT }, // break

    { 0,0,0,0,NULL,0,NULL,0 }
};

//Teclas adicionales spectrum +2,+2a,+3
keyboard_help_double_key keyboard_map_additional_p2[]={
    { 500,162, 537,198,     KEY_PORT_VALUE_SYMBOL,  NULL, 0},  //symbol derecha
    { 452,124,536,158,      KEY_PORT_VALUE_SHIFT,   NULL, 0}, //caps shift derecha
    { 504,42,537,118,       &puerto_49150,1,   NULL, 0}, //trozo del enter parte de arriba
    { 2,82, 68, 120,        KEY_PORT_VALUE_SHIFT,   KEY_PORT_VALUE_SYMBOL }, //extend mode
    { 2,44, 58,80,          &puerto_65278,1,        &puerto_61438,1 },  //delete
    { 2,2,38,36,            &puerto_63486,4,        KEY_PORT_VALUE_SHIFT }, //true video
    { 44,2,76,36,           &puerto_63486,8,        KEY_PORT_VALUE_SHIFT }, //inverse video
    { 62,44,98,80,          &puerto_61438,2,        KEY_PORT_VALUE_SHIFT },//graph
    { 74,82,108,118,        &puerto_63486,1,        KEY_PORT_VALUE_SHIFT },//edit
    { 90,124,128,158,       &puerto_63486,2,        KEY_PORT_VALUE_SHIFT }, //caps lock
    { 42,164,78,198,        &puerto_57342,2,        KEY_PORT_VALUE_SYMBOL }, // ;
    { 82,164,116,198,       &puerto_57342,1,        KEY_PORT_VALUE_SYMBOL }, // "
    { 122,164,158,198,      &puerto_63486,16,       KEY_PORT_VALUE_SHIFT }, // flecha izq
    { 162,164,196,198,      &puerto_61438,4,        KEY_PORT_VALUE_SHIFT }, // flecha der
    { 382,164,418,198,      &puerto_61438,8,        KEY_PORT_VALUE_SHIFT }, // flecha arr
    { 422,164,458,198,      &puerto_61438,16,       KEY_PORT_VALUE_SHIFT }, // flecha abaj
    { 460,162,496,198,      &puerto_32766,8,        KEY_PORT_VALUE_SYMBOL }, // ,
    { 412,124,448,160,      &puerto_32766,4,        KEY_PORT_VALUE_SYMBOL }, // .
    { 482,2,538,38,         &puerto_32766,1,        KEY_PORT_VALUE_SHIFT }, // break

    { 0,0,0,0,NULL,0,NULL,0 }
};

//Teclas adicionales zxuno
keyboard_help_double_key keyboard_map_additional_zxuno[]={
    { 528,4,594,32,   &puerto_65278,1,        &puerto_61438,1 },  //delete
    { 6,82,46,112,    &puerto_63486,2,        KEY_PORT_VALUE_SHIFT }, //caps lock
    { 494,126,590,152,  KEY_PORT_VALUE_SHIFT,   NULL, 0},           //caps shift derecha
    { 546,164,592,194,      KEY_PORT_VALUE_SYMBOL,   NULL, 0 }, //symbol shift derecha

    { 0,0,0,0,NULL,0,NULL,0 }
};

//Teclas adicionales zxevo
keyboard_help_double_key keyboard_map_additional_zxevo[]={
    { 4,9,26,34,        &puerto_63486,1,        KEY_PORT_VALUE_SHIFT },//edit
    { 2,46,42,70,         &puerto_32766,1,        KEY_PORT_VALUE_SHIFT }, // break
    { 5,83,43,107,       &puerto_63486,2,        KEY_PORT_VALUE_SHIFT }, //caps lock
    { 477,7,536,33,          &puerto_65278,1,        &puerto_61438,1 },  //delete

    { 0,0,0,0,NULL,0,NULL,0 }
};

//Teclas adicionales chloe
keyboard_help_double_key keyboard_map_additional_chloe[]={
    { 470,4,534,36,          &puerto_65278,1,        &puerto_61438,1 },  //delete
    { 4,4,32,36,    &puerto_32766,1,        KEY_PORT_VALUE_SHIFT }, // break
    { 4,44,48,74,        &puerto_63486,1,        KEY_PORT_VALUE_SHIFT }, //En Chloe, TAB es shift+1.
    { 2,84,58,114,          &puerto_61438,2,        KEY_PORT_VALUE_SHIFT }, //En Chloe, Control es shift+9
    { 496,162,534,194,         &puerto_61438,2,        KEY_PORT_VALUE_SHIFT }, //En Chloe, Control es shift+9. Control derecha
    { 362,164,400,194,     KEY_PORT_VALUE_SYMBOL,  NULL, 0}, //Alt derecha. alt es symbol?
    { 442,124,536,156,      KEY_PORT_VALUE_SHIFT,   NULL, 0}, //caps shift derecha
    { 48,162,88,196,     KEY_PORT_VALUE_SYMBOL,  NULL, 0}, //winkey izquierda
    { 408,164,444,194,     KEY_PORT_VALUE_SYMBOL,  NULL, 0}, //winkey derecha
    { 4,164,42,194,     &puerto_63486,2,        KEY_PORT_VALUE_SHIFT }, //caps lock

    { 0,0,0,0,NULL,0,NULL,0 }
};

keyboard_help_double_key keyboard_map_additional_tk95[]={
    { 2,96,60,122,      KEY_PORT_VALUE_SHIFT,   KEY_PORT_VALUE_SYMBOL }, //extend mode
    { 502,96,536,120,   &puerto_65278,1,        &puerto_61438,1 },  //delete
    { 414,132,438,152,  KEY_PORT_VALUE_SHIFT,   NULL, 0},           //caps shift derecha
    { 444,24,464,44,    &puerto_63486,4,        KEY_PORT_VALUE_SHIFT }, //true video
    { 476,26,500,46,    &puerto_63486,8,        KEY_PORT_VALUE_SHIFT }, //inverse video
    { 2,60,52,80,       &puerto_61438,2,        KEY_PORT_VALUE_SHIFT },//graph
    { 2,24,36,48,       &puerto_63486,1,        KEY_PORT_VALUE_SHIFT },//edit
    { 56,134,76,154,    &puerto_63486,2,        KEY_PORT_VALUE_SHIFT }, //caps lock
    { 396,98,422,120,   &puerto_57342,2,        KEY_PORT_VALUE_SYMBOL }, // ;
    { 406,24,432,48,    &puerto_57342,1,        KEY_PORT_VALUE_SYMBOL }, // "
    { 434,96,456,118,   &puerto_63486,16,       KEY_PORT_VALUE_SHIFT }, // flecha izq
    { 468,98,492,118,   &puerto_61438,4,        KEY_PORT_VALUE_SHIFT }, // flecha der
    { 424,62,448,82,    &puerto_61438,8,        KEY_PORT_VALUE_SHIFT }, // flecha arr
    { 458,60,482,80,    &puerto_61438,16,       KEY_PORT_VALUE_SHIFT }, // flecha abaj
    { 380,134,402,156,  &puerto_32766,8,        KEY_PORT_VALUE_SYMBOL }, // ,
    { 344,134,368,156,  &puerto_32766,4,        KEY_PORT_VALUE_SYMBOL }, // .
    { 512,24,536,46,    &puerto_32766,1,        KEY_PORT_VALUE_SHIFT }, // break

    { 0,0,0,0,NULL,0,NULL,0 }
};

keyboard_help_double_key keyboard_map_additional_next[]={
    { 504,163,539,198,     KEY_PORT_VALUE_SYMBOL,  NULL, 0},  //symbol derecha
    { 466,123,538,159,      KEY_PORT_VALUE_SHIFT,   NULL, 0}, //caps shift derecha
    { 503,43,538,120,       &puerto_49150,1,   NULL, 0}, //trozo del enter parte de arriba
    { 91,124,137,161,        KEY_PORT_VALUE_SHIFT,   KEY_PORT_VALUE_SYMBOL }, //extend mode
    { 484,3,538,41,          &puerto_65278,1,        &puerto_61438,1 },  //delete
    { 2,43,46,79,            &puerto_63486,4,        KEY_PORT_VALUE_SHIFT }, //true video
    { 51,42,96,80,           &puerto_63486,8,        KEY_PORT_VALUE_SHIFT }, //inverse video
    { 61,83,106,118,          &puerto_61438,2,        KEY_PORT_VALUE_SHIFT },//graph
    { 40,2,76,38,        &puerto_63486,1,        KEY_PORT_VALUE_SHIFT },//edit
    { 2,84,56,118,       &puerto_63486,2,        KEY_PORT_VALUE_SHIFT }, //caps lock
    { 42,165,77,198,        &puerto_57342,2,        KEY_PORT_VALUE_SYMBOL }, // ;
    { 81,165,118,198,       &puerto_57342,1,        KEY_PORT_VALUE_SYMBOL }, // "
    { 386,165,422,198,      &puerto_63486,16,       KEY_PORT_VALUE_SHIFT }, // flecha izq
    { 466,164,501,198,      &puerto_61438,4,        KEY_PORT_VALUE_SHIFT }, // flecha der
    { 425,123,463,159,      &puerto_61438,8,        KEY_PORT_VALUE_SHIFT }, // flecha arr
    { 425,165,462,198,      &puerto_61438,16,       KEY_PORT_VALUE_SHIFT }, // flecha abaj
    { 121,165,159,198,      &puerto_32766,8,        KEY_PORT_VALUE_SYMBOL }, // ,
    { 163,164,201,198,      &puerto_32766,4,        KEY_PORT_VALUE_SYMBOL }, // .
    { 2,2,37,39,          &puerto_32766,1,        KEY_PORT_VALUE_SHIFT }, // break

    { 0,0,0,0,NULL,0,NULL,0 }
};

keyboard_help_double_key keyboard_map_additional_sms[]={
    { 164,100,188,122,      &puerto_65278,4,   NULL,0 }, //segundo boton=tecla X

    { 0,0,0,0,NULL,0,NULL,0 }
};

keyboard_help_double_key keyboard_map_additional_sg1000[]={
    { 66,46,80,68,      &puerto_65278,4,   NULL,0 }, //segundo boton=tecla X

    { 0,0,0,0,NULL,0,NULL,0 }
};

keyboard_help_double_key keyboard_map_additional_msx[]={
    { 356,120,398,142,  &msx_keyboard_table[6], 1,   NULL,0 }, //shift derecho

    { 0,0,0,0,NULL,0,NULL,0 }
};

keyboard_help_double_key keyboard_map_additional_svi_318[]={
    { 354,112,386,126, &svi_keyboard_table[6], 1,   NULL,0 }, //shift derecho
    { 0,0,0,0,NULL,0,NULL,0 }
};

keyboard_help_double_key keyboard_map_additional_svi_328[]={
    { 358,118,388,136, &svi_keyboard_table[6], 1,   NULL,0 }, //shift derecho
    { 518,116,536,162, &svi_keyboard_table[6], 64,  NULL,0 }, //enter derecho

    { 0,0,0,0,NULL,0,NULL,0 }
};

keyboard_help_double_key keyboard_map_additional_timex_computer[]={
    { 480,134,526,154,      KEY_PORT_VALUE_SHIFT,   NULL, 0}, //caps shift derecha

    { 438,134,470,156,      &puerto_32766,1,        KEY_PORT_VALUE_SHIFT }, // break

    { 0,0,0,0,NULL,0,NULL,0 }
};

keyboard_help_double_key keyboard_map_additional_cpc_464[]={
    { 436,185,496,205,  &cpc_keyboard_table[2], 32,   NULL,0 }, //shift derecho

    { 0,0,0,0,NULL,0,NULL,0 }
};

keyboard_help_double_key keyboard_map_additional_cpc_664[]={
    { 436,164,496,184,  &cpc_keyboard_table[2], 32,   NULL,0 }, //shift derecho

    { 0,0,0,0,NULL,0,NULL,0 }
};

keyboard_help_double_key keyboard_map_additional_cpc_6128[]={
    { 476,181,525,202,  &cpc_keyboard_table[2], 32,   NULL,0 }, //shift derecho

    { 0,0,0,0,NULL,0,NULL,0 }
};

keyboard_help_double_key keyboard_map_additional_pcw[]={
    { 445,166,490,187,  &pcw_keyboard_table[2], 32,   NULL,0 }, //shift derecho

    { 0,0,0,0,NULL,0,NULL,0 }
};

//Nota: no soportamos el boton joystick en la izquierda porque requeriria entrada en esta tecla adicional
//pero en ese caso es una pulsacion de puesta a 1 el bit, y en teclas dobles se pone a 0 los bits
keyboard_help_double_key keyboard_map_additional_coleco[]={
    { 16,78,29,88,      &puerto_63486, 1,   NULL,0 }, //1
    { 30,76,44,86,      &puerto_63486, 2,   NULL,0 }, //2
    { 45,74,58,84,      &puerto_63486, 4,   NULL,0 }, //3
    {16,94,28,106,      &puerto_63486, 8,   NULL,0 }, //4
    {32,92,44,102,      &puerto_63486,16,   NULL,0 }, //5
    {48,90,62,100,      &puerto_61438,16,   NULL,0 }, //6
    {16,110,28,118,     &puerto_61438, 8,   NULL,0 }, //7
    {34,108,44,116,     &puerto_61438, 4,   NULL,0 }, //8
    {48,106,60,114,     &puerto_61438, 2,   NULL,0 }, //9
    {16,126,30,138,     &puerto_65278, 4,   NULL,0 }, //*
    {32,124,46,136,     &puerto_61438, 1,   NULL,0 }, //0
    {50,122,64,134,     &puerto_65278, 2,   NULL,0 }, //#

    { 0,0,0,0,NULL,0,NULL,0 }
};


/*
z80_byte puerto_65534=255;
	D4	D3	D2	D1	D0
	RIGHT	LEFT	DOWN	UP	CTRL
	z80_byte puerto_teclado_sam_fef9=255;
z80_byte puerto_teclado_sam_fdf9=255;
z80_byte puerto_teclado_sam_fbf9=255;
z80_byte puerto_teclado_sam_f7f9=255;


z80_byte puerto_teclado_sam_eff9=255;
z80_byte puerto_teclado_sam_dff9=255;
z80_byte puerto_teclado_sam_bff9=255;
z80_byte puerto_teclado_sam_7ff9=255;
*/
keyboard_help_double_key keyboard_map_additional_sam[]={

    { 66,152,105,177, &puerto_65534,1,NULL,0 },

	{577,115,599,140, &puerto_65534,2,NULL,0 },
	{578,155,599,177, &puerto_65534,4,NULL,0 },
	{541,152,563,177, &puerto_65534,8,NULL,0 },
	{614,154,637,176, &puerto_65534,16,NULL,0 },

	{541,80,562,103, &puerto_teclado_sam_fef9,32,NULL,0},
	{577,78,599,103, &puerto_teclado_sam_fef9,64,NULL,0},
	{613,80,636,105, &puerto_teclado_sam_fef9,128,NULL,0},

	{542,43,562,66,&puerto_teclado_sam_fdf9,32,NULL,0},
	{578,43,600,67,&puerto_teclado_sam_fdf9,64,NULL,0},
	{613,40,635,68,&puerto_teclado_sam_fdf9,128,NULL,0},

	{543,6,563,28,&puerto_teclado_sam_fbf9,32,NULL,0},
	{578,4,600,30,&puerto_teclado_sam_fbf9,64,NULL,0},
	{614,3,636,30,&puerto_teclado_sam_fbf9,128,NULL,0},

	{3,4,23,27, &puerto_teclado_sam_f7f9,32,NULL,0},
	{4,41,43,64, &puerto_teclado_sam_f7f9,64,NULL,0},
	{4,78,51,104, &puerto_teclado_sam_f7f9,128,NULL,0},

	{412,4,433,26, &puerto_teclado_sam_eff9,32,NULL,0},
	{449,2,470,26, &puerto_teclado_sam_eff9,64,NULL,0},
	{486,4,526,25, &puerto_teclado_sam_eff9,128,NULL,0},

	{430,41,454,66, &puerto_teclado_sam_dff9,32,NULL,0},
	{468,40,490,65, &puerto_teclado_sam_dff9,64,NULL,0},
	{541,115,565,140,&puerto_teclado_sam_dff9,128,NULL,0},

	{401,78,424,103, &puerto_teclado_sam_bff9,32,NULL,0},
	{440,78,461,102, &puerto_teclado_sam_bff9,64,NULL,0},
	{421,154,463,177,&puerto_teclado_sam_bff9,128,NULL,0},

	{345,116,366,140, &puerto_teclado_sam_7ff9,32,NULL,0},
	{382,116,406,140, &puerto_teclado_sam_7ff9,64,NULL,0},
	{420,116,444,141, &puerto_teclado_sam_7ff9,128,NULL,0},

	//Return, shift, symbol derechos
	{503,41,529,105, &puerto_49150,1,   NULL, 0},
	{460,117,529,141, KEY_PORT_VALUE_SHIFT,   NULL, 0},
	{477,155,528,177, KEY_PORT_VALUE_SYMBOL,  NULL, 0},

	{ 0,0,0,0,NULL,0,NULL,0 }
};

//teclas QL
// ================================== matrix ============================
//        0      1      2      3      4      5      6      7
//  +-------------------------------------------------------
// 7|    F4     F1      5     F2     F3     F5      4      7     ql_keyboard_table[0]
// 6|   Ret   Left     Up    Esc  Right      \  Space   Down     ql_keyboard_table[1]
// 5|     ]      z      .      c      b  Pound      m      '     ql_keyboard_table[2]
// 4|     [   Caps      k      s      f      =      g      ;     ql_keyboard_table[3]
// 3|     l      3      h      1      a      p      d      j     ql_keyboard_table[4]
// 2|     9      w      i    Tab      r      -      y      o     ql_keyboard_table[5]
// 1|     8      2      6      q      e      0      t      u     ql_keyboard_table[6]
// 0| Shift   Ctrl    Alt      x      v      /      n      ,     ql_keyboard_table[7]



int keyboard_map_table_coords_ql[64*4]={
    2,102,30,130,2,2,31,30,216,2,244,30,2,35,31,64,2,69,31,97,2,136,29,166,182,2,211,32,281,2,310,32, //primera fila: F4,F1, 5, F2...
    469,67,525,99,105,136,136,166,405,135,434,164,47,1,77,30,140,135,168,165,510,2,538,28,173,135,401,163,438,135,468,163,
    462,35,492,63,121,102,151,131,386,101,417,130,189,101,219,128,255,101,285,130,478,1,507,29,322,101,351,127,437,68,467,96,
    428,35,459,63,47,70,101,96,338,68,367,99,137,67,170,97,205,68,235,97,444,2,474,30,238,68,269,97,405,68,434,97,
    371,68,400,95,147,2,177,30,272,68,300,97,82,2,111,29,104,70,135,99,396,35,426,64,172,67,204,96,305,68,336,98,
    346,1,374,30,130,35,160,63,330,35,360,65,45,35,95,64,198,36,228,64,412,2,442,30,263,35,293,64,363,34,393,63,
    314,1,342,29,114,2,145,30,247,2,278,31,97,36,127,64,165,35,194,65,380,2,409,32,231,35,262,65,297,36,327,65,
    46,102,118,130,44,137,100,165,471,134,525,165,155,101,184,129,221,101,251,129,421,101,452,129,288,101,318,130,355,102,385,129,
};

//Teclas adicionales QL
keyboard_help_double_key keyboard_map_additional_ql[]={
    { 455,102,524,129,     &ql_keyboard_table[7],1,  NULL, 0},  //shift derecha
    { 493,35,524,97, &ql_keyboard_table[1],1,NULL,0 }, //Enter en vertical
    { 0,0,0,0,NULL,0,NULL,0 }
};


/*
Z88

-------------------------------------------------------------------------
         | D7     D6      D5      D4      D3      D2      D1      D0
-------------------------------------------------------------------------
A15 (#7) | RSH    SQR     ESC     INDEX   CAPS    .       /       £
A14 (#6) | HELP   LSH     TAB     DIA     MENU    ,       ;       '
A13 (#5) | [      SPACE   1       Q       A       Z       L       0
A12 (#4) | ]      LFT     2       W       S       X       M       P
A11 (#3) | -      RGT     3       E       D       C       K       9
A10 (#2) | =      DWN     4       R       F       V       J       O
A9  (#1) | \      UP      5       T       G       B       U       I
A8  (#0) | DEL    ENTER   6       Y       H       N       7       8
-------------------------------------------------------------------------

*/

int keyboard_map_table_coords_z88[64*4]={
    462,70,494,100, 406,106,436,138, 370,106,402,136, 394,140,428,168, 2,142,32,168, 2,2,28,26, 104,144,136,168, 446,106,502,136,
    424,70,456,102, 386,70,420,100, 332,106,366,136, 32,140,64,168, 2,70,56,96, 2,36,46,62, 2,108,74,136, 68,142,102,168,
    364,2,392,26, 352,70,380,98, 74,108,108,136, 60,72,92,96, 52,36,84,64, 32,2,64,26, 140,144,394,168, 418,34,450,62,
    380,34,414,64, 296,106,326,136, 114,106,144,138,  94,68,126,100, 88,34,116,60, 66,2,98,26, 432,142,464,168, 456,36,488,62,
    328,2,356,26, 316,70,346,98, 150,106,180,134, 132,70,164,100, 124,36,154,62, 106,2,134,26, 470,142,504,168, 400,2,430,28,
    344,34,374,62, 276,72,308,98, 184,104,218,134, 168,70,196,98, 162,36,192,62, 142,2,170,28, 506,144,537,168, 438,2,468,28,
    306,34,338,62, 270,34,304,62, 222,106,252,132, 204,72,234,98, 196,36,228,62, 180,2,206,28, 508,106,537,134, 474,2,504,28,
    288,2,318,30,  252,2,282,26,  258,106,290,134, 240,72,272,100, 234,34,266,62, 214,2,246,30, 496,36,537,98, 510,2,537,28,
};

//Teclas simples mantenidas pulsadas. A no 0 para que se queden pulsadas
//10 filas * 8 columnas max (CPC el que tiene mas)
#define KEYBOARD_HELP_MAX_TECLAS_MANTENIDAS_PULSADAS (10*8)
//64
int keyboard_help_teclas_mantenidas_pulsadas_simples[KEYBOARD_HELP_MAX_TECLAS_MANTENIDAS_PULSADAS];
int keyboard_help_teclas_mantenidas_pulsadas_dobles[KEYBOARD_HELP_MAX_TECLAS_MANTENIDAS_PULSADAS];

//Funciones para leer y escribir de esas teclas comprobando que no nos salgamos de los limites
int keyboard_get_teclas_mantenidas_pulsadas_simples(int indice)
{
    if (indice<KEYBOARD_HELP_MAX_TECLAS_MANTENIDAS_PULSADAS) return keyboard_help_teclas_mantenidas_pulsadas_simples[indice];
    else {
        debug_printf(VERBOSE_DEBUG,"Trying to get kept pressed simple key when index beyond limit: %d",indice);
        return 0;
    }
}

int keyboard_get_teclas_mantenidas_pulsadas_dobles(int indice)
{
    if (indice<KEYBOARD_HELP_MAX_TECLAS_MANTENIDAS_PULSADAS) return keyboard_help_teclas_mantenidas_pulsadas_dobles[indice];
    else {
        debug_printf(VERBOSE_DEBUG,"Trying to get kept pressed double key when index beyond limit: %d",indice);
        return 0;
    }
}

void keyboard_set_teclas_mantenidas_pulsadas_simples(int indice,int valor)
{
    if (indice<KEYBOARD_HELP_MAX_TECLAS_MANTENIDAS_PULSADAS) keyboard_help_teclas_mantenidas_pulsadas_simples[indice]=valor;
    else debug_printf(VERBOSE_DEBUG,"Trying to set kept pressed simple key when index beyond limit: %d",indice);
}

void keyboard_set_teclas_mantenidas_pulsadas_dobles(int indice,int valor)
{
    if (indice<KEYBOARD_HELP_MAX_TECLAS_MANTENIDAS_PULSADAS) keyboard_help_teclas_mantenidas_pulsadas_dobles[indice]=valor;
    else debug_printf(VERBOSE_DEBUG,"Trying to set kept pressed double key when index beyond limit: %d",indice);
}

void keyboard_help_reset_teclas_pulsadas(void)
{
    int i;

    for (i=0;i<KEYBOARD_HELP_MAX_TECLAS_MANTENIDAS_PULSADAS;i++) {
        keyboard_help_teclas_mantenidas_pulsadas_simples[i]=0;
        keyboard_help_teclas_mantenidas_pulsadas_dobles[i]=0;
    }


}

//Retorna tabla de coordenadas de teclas
int *keyboard_help_return_map_table(void)
{
    if (MACHINE_IS_SPECTRUM_48_PLUS_SPA || MACHINE_IS_SPECTRUM_48_PLUS_ENG ||
        MACHINE_IS_INVES || MACHINE_IS_SPECTRUM_128 || MACHINE_IS_SPECTRUM_128_SPA || MACHINE_IS_CZ_SPECTRUM_PLUS) {
        return keyboard_map_table_coords_48p;
    }

    else if (MACHINE_IS_SPECTRUM_P2 || MACHINE_IS_SPECTRUM_P2A_P3) {
        return keyboard_map_table_coords_p2;
    }

    else if (MACHINE_IS_TBBLUE) {
        return keyboard_map_table_coords_next;
    }

    else if (MACHINE_IS_ZXUNO) {
        return keyboard_map_table_coords_zxuno;
    }

    else if (MACHINE_IS_ZXEVO) {
        return keyboard_map_table_coords_zxevo;
    }

    else if (MACHINE_IS_CHLOE) {
        return keyboard_map_table_coords_chloe;
    }

    else if (MACHINE_IS_PENTAGON) {
        return keyboard_map_table_coords_pentagon;
    }

    else if (MACHINE_IS_MICRODIGITAL_TK85) {
        return keyboard_map_table_coords_tk85;
    }

    else if (MACHINE_IS_TIMEX_TC2048 || MACHINE_IS_TIMEX_TC2068 || MACHINE_IS_TIMEX_TS2068) {
        return keyboard_map_table_coords_timex_computer;
    }

    else if (MACHINE_IS_TIMEX_TS1500 || MACHINE_IS_CZ_1500) {
        return keyboard_map_table_coords_timex_sinclair_1500;
    }

    else if (MACHINE_IS_CZ_1000_PLUS) {
        return keyboard_map_table_coords_cz1000_plus;
    }

    else if (MACHINE_IS_CZ_1500_PLUS) {
        return keyboard_map_table_coords_cz1500_plus;
    }

    else if (MACHINE_IS_ZX8081) {
        return keyboard_map_table_coords_zx8081;
    }

    else if (MACHINE_IS_ACE) {
        return keyboard_map_table_coords_ace;
    }

    else if (MACHINE_IS_QL) {
        return keyboard_map_table_coords_ql;
    }

    else if (MACHINE_IS_Z88) {
        return keyboard_map_table_coords_z88;
    }

    else if (MACHINE_IS_MICRODIGITAL_TK90X || MACHINE_IS_MICRODIGITAL_TK90X_SPA) {
        return keyboard_map_table_coords_tk90x;
    }

    else if (MACHINE_IS_MICRODIGITAL_TK95 || MACHINE_IS_MICRODIGITAL_TK95_SPA) {
        return keyboard_map_table_coords_tk95;
    }

    else if (MACHINE_IS_SMS) {
        return keyboard_map_table_coords_sms;
    }

    else if (MACHINE_IS_COLECO) {
        return keyboard_map_table_coords_coleco;
    }

    else if (MACHINE_IS_MSX) {
        return keyboard_map_table_coords_msx;
    }

    else if (MACHINE_IS_SVI_318) {
        return keyboard_map_table_coords_svi_318;
    }

    else if (MACHINE_IS_SVI_328) {
        return keyboard_map_table_coords_svi_328;
    }

    else if (MACHINE_IS_SAM) {
        return keyboard_map_table_coords_sam;
    }

    else if (MACHINE_IS_CPC_464 || MACHINE_IS_CPC_4128) {
        return keyboard_map_table_coords_cpc_464;
    }

    else if (MACHINE_IS_CPC_664) {
        return keyboard_map_table_coords_cpc_664;
    }

    else if (MACHINE_IS_CPC_6128) {
        return keyboard_map_table_coords_cpc_6128;
    }

    else if (MACHINE_IS_PCW) {
        return keyboard_map_table_coords_pcw;
    }

    else if (MACHINE_IS_SG1000) {
        return keyboard_map_table_coords_sg1000;
    }

    else if (MACHINE_IS_MK14) {
        return keyboard_map_table_coords_mk14;
    }

    else return keyboard_map_table_coords_48;
}


keyboard_help_double_key *keyboard_help_return_double_keys(void)
{

    keyboard_help_double_key *teclas_dobles=NULL;

    if (MACHINE_IS_SPECTRUM_48_PLUS_SPA || MACHINE_IS_SPECTRUM_48_PLUS_ENG ||
        MACHINE_IS_INVES || MACHINE_IS_SPECTRUM_128 || MACHINE_IS_SPECTRUM_128_SPA || MACHINE_IS_CZ_SPECTRUM_PLUS) {
            teclas_dobles=keyboard_map_additional_48p;
    }

    else if (MACHINE_IS_MICRODIGITAL_TK95 || MACHINE_IS_MICRODIGITAL_TK95_SPA) {
        return keyboard_map_additional_tk95;
    }

    else if (MACHINE_IS_TBBLUE) {
        return keyboard_map_additional_next;
    }

    else if (MACHINE_IS_SPECTRUM_P2 || MACHINE_IS_SPECTRUM_P2A_P3) {
        return keyboard_map_additional_p2;
    }

    else if (MACHINE_IS_ZXUNO) {
        return keyboard_map_additional_zxuno;
    }

    else if (MACHINE_IS_ZXEVO) {
        return keyboard_map_additional_zxevo;
    }

    else if (MACHINE_IS_CHLOE) {
        return keyboard_map_additional_chloe;
    }

    else if (MACHINE_IS_TIMEX_TC2048 || MACHINE_IS_TIMEX_TC2068 || MACHINE_IS_TIMEX_TS2068) {
        return keyboard_map_additional_timex_computer;
    }

    else if (MACHINE_IS_QL) {
        return keyboard_map_additional_ql;
    }

    else if (MACHINE_IS_SMS) {
        return keyboard_map_additional_sms;
    }

    else if (MACHINE_IS_COLECO) {
        return keyboard_map_additional_coleco;
    }

    else if (MACHINE_IS_SG1000) {
        return keyboard_map_additional_sg1000;
    }

    else if (MACHINE_IS_MSX) {
        return keyboard_map_additional_msx;
    }

    else if (MACHINE_IS_SVI_318) {
        return keyboard_map_additional_svi_318;
    }

    else if (MACHINE_IS_SVI_328) {
        return keyboard_map_additional_svi_328;
    }

    else if (MACHINE_IS_SAM) {
        return keyboard_map_additional_sam;
    }

    else if (MACHINE_IS_CPC_464 || MACHINE_IS_CPC_4128) {
        return keyboard_map_additional_cpc_464;
    }

    else if (MACHINE_IS_CPC_664) {
        return keyboard_map_additional_cpc_664;
    }

    else if (MACHINE_IS_CPC_6128) {
        return keyboard_map_additional_cpc_6128;
    }

    else if (MACHINE_IS_PCW) {
        return keyboard_map_additional_pcw;
    }

    return teclas_dobles;

}

//tabla para puertos
z80_byte *keyboard_map_ports_table_speccy[8]={
    &puerto_65278,&puerto_65022,&puerto_64510,&puerto_63486,
    &puerto_61438,&puerto_57342,&puerto_49150,&puerto_32766
};

z80_byte *keyboard_map_ports_table_ql[8]={
    &ql_keyboard_table[0],&ql_keyboard_table[1],&ql_keyboard_table[2],&ql_keyboard_table[3],
    &ql_keyboard_table[4],&ql_keyboard_table[5],&ql_keyboard_table[6],&ql_keyboard_table[7]
};

z80_byte *keyboard_map_ports_table_z88[8]={
    &blink_kbd_a15,&blink_kbd_a14,&blink_kbd_a13,&blink_kbd_a12,
    &blink_kbd_a11,&blink_kbd_a10,&blink_kbd_a9,&blink_kbd_a8
};

z80_byte *keyboard_map_ports_table_joystick[1]={
    &puerto_especial_joystick
};

z80_byte *keyboard_map_ports_table_mk14[8]={
    &mk14_keystatus[0],&mk14_keystatus[1],&mk14_keystatus[2],&mk14_keystatus[3],
    &mk14_keystatus[4],&mk14_keystatus[5],&mk14_keystatus[6],&mk14_keystatus[7]
};

z80_byte *keyboard_map_ports_table_msx[9]={
    &msx_keyboard_table[0],&msx_keyboard_table[1],&msx_keyboard_table[2],&msx_keyboard_table[3],
    &msx_keyboard_table[4],&msx_keyboard_table[5],&msx_keyboard_table[6],&msx_keyboard_table[7],
    &msx_keyboard_table[8]
};

z80_byte *keyboard_map_ports_table_svi[11]={
    &svi_keyboard_table[0],&svi_keyboard_table[1],&svi_keyboard_table[2],&svi_keyboard_table[3],
    &svi_keyboard_table[4],&svi_keyboard_table[5],&svi_keyboard_table[6],&svi_keyboard_table[7],
    &svi_keyboard_table[8], &svi_keyboard_table[9], &svi_keyboard_table[10]
};

z80_byte *keyboard_map_ports_table_cpc[10]={
    &cpc_keyboard_table[0],&cpc_keyboard_table[1],&cpc_keyboard_table[2],&cpc_keyboard_table[3],
    &cpc_keyboard_table[4],&cpc_keyboard_table[5],&cpc_keyboard_table[6],&cpc_keyboard_table[7],
    &cpc_keyboard_table[8],&cpc_keyboard_table[9]
};

z80_byte *keyboard_map_ports_table_pcw[11]={
    &pcw_keyboard_table[0],&pcw_keyboard_table[1],&pcw_keyboard_table[2],&pcw_keyboard_table[3],
    &pcw_keyboard_table[4],&pcw_keyboard_table[5],&pcw_keyboard_table[6],&pcw_keyboard_table[7],
    &pcw_keyboard_table[8],&pcw_keyboard_table[9],&pcw_keyboard_table[10]
};

/*
void menu_help_keyboard_show_all_keys(zxvision_window *ventana,int keyboard_map_coords[],int total_keys)
{
    int i;

    for (i=0;i<total_keys;i++) {
        int offset=4*i;
        int x1=keyboard_map_coords[offset];
        int y1=keyboard_map_coords[offset+1];
        int ancho=keyboard_map_coords[offset+2]-x1+1;
        int alto=keyboard_map_coords[offset+3]-y1+1;

        zxvision_draw_rectangle_function(ventana,x1,y1,ancho,alto,3,zxvision_putpixel_no_zoom);
        zxvision_draw_rectangle_function(ventana,x1-1,y1-1,ancho+2,alto+2,3,zxvision_putpixel_no_zoom);
    }
}
*/

void menu_help_draw_rectangle_key(zxvision_window *ventana,int x1,int y1,int ancho,int alto,int color)
{
    //Recuadro del tamanyo y algo mas grande
    zxvision_draw_rectangle_function(ventana,x1,y1,ancho,alto,color,zxvision_putpixel_no_zoom);
    zxvision_draw_rectangle_function(ventana,x1-1,y1-1,ancho+2,alto+2,color,zxvision_putpixel_no_zoom);
    zxvision_draw_rectangle_function(ventana,x1-2,y1-2,ancho+4,alto+4,color,zxvision_putpixel_no_zoom);
    //Y algo mas pequenyo
    zxvision_draw_rectangle_function(ventana,x1+1,y1+1,ancho-2,alto-2,color,zxvision_putpixel_no_zoom);
}

//Dice si bit tecla pulsada, dependiendo si es valor 0 en la mayoria de maquinas, o si no es 0 (como puertos de joystick en master system)
int menu_help_keyboard_is_pressed_bit_key(z80_byte valor)
{
    if (MACHINE_IS_SMS || MACHINE_IS_SG1000 || MACHINE_IS_COLECO) {
        if (valor) return 1;
        else return 0;
    }
    else {
        if (!valor) return 1;
        else return 0;
    }
}

void menu_help_keyboard_show_speccy_pressed_keys(zxvision_window *ventana,int keyboard_map_coords[],int total_columnas,int total_filas,
    z80_byte *key_map_ports[],keyboard_help_double_key *teclas_dobles)
{
    int fila_tecla;
    int columna_tecla;

    //int total_columnas=5;

    int indice_tecla_mantenida_pulsada=0;

    for (fila_tecla=0;fila_tecla<total_filas;fila_tecla++) {
        z80_byte *puerto=key_map_ports[fila_tecla];
        int mascara=1;
        for (columna_tecla=0;columna_tecla<total_columnas;columna_tecla++,indice_tecla_mantenida_pulsada++) {
            //Tecla pulsada segun el puerto
            int tecla_encontrada=0;
            z80_byte valor=*puerto;
            valor &=mascara;

            int color=3;

            if (menu_help_keyboard_is_pressed_bit_key(valor)) {
                tecla_encontrada=1;
                menu_help_keyboard_overlay_force_draw=1;
            }

            //Tecla pulsada mantenida


            if (keyboard_get_teclas_mantenidas_pulsadas_simples(indice_tecla_mantenida_pulsada)) {
                tecla_encontrada=1;
                //printf("Encontrada pulsada simple %d\n",indice_tecla_mantenida_pulsada);
                color=1;
            }



            if (tecla_encontrada) {
                //printf("forzar dibujar\n");
                //menu_help_keyboard_overlay_force_draw=1;

                int offset=fila_tecla*total_columnas+columna_tecla; //Indice a tecla

                //indice a coordenada
                offset *=4;

                int x1=keyboard_map_coords[offset];
                int y1=keyboard_map_coords[offset+1];
                int ancho=keyboard_map_coords[offset+2]-x1+1;
                int alto=keyboard_map_coords[offset+3]-y1+1;



                menu_help_draw_rectangle_key(ventana,x1,y1,ancho,alto,color);
            }
            mascara=mascara<<1;
        }

    }

    if (teclas_dobles!=NULL) {
        int indice_tecla_mantenida_pulsada=0;
        int i=0;
        while(teclas_dobles[i].puerto1!=NULL) {
            z80_byte *puerto1=teclas_dobles[i].puerto1;
            z80_byte *puerto2=teclas_dobles[i].puerto2;

            z80_byte mascara1=teclas_dobles[i].mascara1;
            z80_byte valor1=(*puerto1) & mascara1;

            z80_byte mascara2=teclas_dobles[i].mascara2;


            int tecla_encontrada=0;
            int color=3;

            //Si realmente no es tecla doble sino adicional (como symbol derecha)
            if (puerto2==NULL) {

                //teclas dobles siempre se asume que si pulsada, bit=0
                if (valor1==0) {
                    tecla_encontrada=1;
                    menu_help_keyboard_overlay_force_draw=1;
                    //printf("force draw\n");
                }
            }

            else {
                z80_byte valor2=(*puerto2) & mascara2;
                //teclas dobles siempre se asume que si pulsada, bit=0
                if (valor1==0 && valor2==0) {
                    tecla_encontrada=1;
                    menu_help_keyboard_overlay_force_draw=1;
                }
            }

            //Tecla pulsada mantenida

            if (keyboard_get_teclas_mantenidas_pulsadas_dobles(indice_tecla_mantenida_pulsada)) {
                tecla_encontrada=1;
                //printf("Encontrada pulsada doble %d\n",indice_tecla_mantenida_pulsada);
                color=1;
            }


            if (tecla_encontrada) {

                int x1=teclas_dobles[i].x1;
                int y1=teclas_dobles[i].y1;
                int ancho=teclas_dobles[i].x2-x1+1;
                int alto=teclas_dobles[i].y2-y1+1;



                //menu_help_keyboard_overlay_force_draw=1;

                menu_help_draw_rectangle_key(ventana,x1,y1,ancho,alto,color);
            }

            i++;
            indice_tecla_mantenida_pulsada++;
        }
    }
}


void menu_help_keyboard_locate_speccy_pressed_keys(int keyboard_map_coords[],z80_byte *key_map_ports[],int total_columnas,int total_filas,
    int x,int y,z80_byte **p_puerto1,z80_byte *p_mascara1,z80_byte **p_puerto2,z80_byte *p_mascara2,
    keyboard_help_double_key *teclas_dobles,int *p_indice_a_tecla_simple,int *p_indice_a_tecla_doble,int *es_tecla_doble)
{

    *p_puerto1=NULL;
    *p_puerto2=NULL;
    *p_indice_a_tecla_simple=-1;
    *p_indice_a_tecla_doble=-1;

    *es_tecla_doble=0;

    int fila_tecla;
    int columna_tecla;

    int indice_a_tecla=0;

    for (fila_tecla=0;fila_tecla<total_filas;fila_tecla++) {
        z80_byte *puerto=key_map_ports[fila_tecla];
        int mascara=1;
        for (columna_tecla=0;columna_tecla<total_columnas;columna_tecla++,indice_a_tecla++) {

            int offset=fila_tecla*total_columnas+columna_tecla; //Indice a tecla

            //indice a coordenada
            offset *=4;

            int x1=keyboard_map_coords[offset];
            int y1=keyboard_map_coords[offset+1];
            int x2=keyboard_map_coords[offset+2];
            int y2=keyboard_map_coords[offset+3];
            if (x>=x1 && x<=x2 && y>=y1 && y<=y2) {
                *p_puerto1=puerto;
                *p_mascara1=mascara;
                *p_indice_a_tecla_simple=indice_a_tecla;
                return;
            }


            mascara=mascara<<1;
        }

    }

    if (teclas_dobles!=NULL) {
        int i=0;
        indice_a_tecla=0;
        while(teclas_dobles[i].puerto1!=NULL) {

            int x1=teclas_dobles[i].x1;
            int y1=teclas_dobles[i].y1;
            int x2=teclas_dobles[i].x2;
            int y2=teclas_dobles[i].y2;

            if (x>=x1 && x<=x2 && y>=y1 && y<=y2) {
                *p_puerto1=teclas_dobles[i].puerto1;
                *p_mascara1=teclas_dobles[i].mascara1;
                *p_puerto2=teclas_dobles[i].puerto2;
                *p_mascara2=teclas_dobles[i].mascara2;
                *p_indice_a_tecla_doble=indice_a_tecla;
                *es_tecla_doble=1;
                return;
            }

            i++;
            indice_a_tecla++;
        }
    }

}

//Activar un bit de tecla segun si se pone a 0 (la mayoria de maquinas) o se pone a 1 (por ejemplo joystick en la master system)
void menu_help_keyboard_activate_bit(z80_byte *puerto,z80_byte mascara)
{
    if (MACHINE_IS_SMS || MACHINE_IS_SG1000 || MACHINE_IS_COLECO) {
        *puerto |=mascara;
    }

    else *puerto &=(255-mascara);
}



int help_keyboard_valor_contador_segundo_anterior;

zxvision_window *menu_help_keyboard_overlay_window;


z80_byte **get_keyboard_map_ports_table(int *total_columnas,int *total_filas)
{
    *total_columnas=5;
    *total_filas=8;

    if (MACHINE_IS_QL) {
        *total_columnas=8;
        return keyboard_map_ports_table_ql;
    }

    if (MACHINE_IS_Z88) {
        *total_columnas=8;
        return keyboard_map_ports_table_z88;
    }

    if (MACHINE_IS_SMS || MACHINE_IS_SG1000 || MACHINE_IS_COLECO) {
        *total_columnas=6;
        *total_filas=1;
        return keyboard_map_ports_table_joystick;
    }

    if (MACHINE_IS_MK14) {
        *total_columnas=4;
        *total_filas=8;
        return keyboard_map_ports_table_mk14;
    }

    if (MACHINE_IS_MSX) {
        *total_columnas=8;
        *total_filas=9;
        return keyboard_map_ports_table_msx;
    }

    if (MACHINE_IS_SVI) {
        *total_columnas=8;
        *total_filas=11;
        return keyboard_map_ports_table_svi;
    }

    if (MACHINE_IS_CPC) {
        *total_columnas=8;
        *total_filas=10;
        return keyboard_map_ports_table_cpc;
    }

    if (MACHINE_IS_PCW) {
        *total_columnas=8;
        *total_filas=11;
        return keyboard_map_ports_table_pcw;
    }

    return keyboard_map_ports_table_speccy;
}

int menu_help_keyboard_highlight_key=0;
int menu_help_keyboard_highlight_key_x1=0;
int menu_help_keyboard_highlight_key_x2=0;
int menu_help_keyboard_highlight_key_y1=0;
int menu_help_keyboard_highlight_key_y2=0;

//Un indice unico por tecla por saber si la anterior iluminada era la misma
int menu_help_keyboard_highlight_key_indice_anterior=-1;


void menu_help_keyboard_overlay_render_512k_pcw_putpixel(zxvision_window *ventana,int x,int y,int color_final,int follow_zoom)
{
    //printf("%d %d\n",x,y);
    if (!follow_zoom) {
        //printf("no follow zoom. %d,%d\n",zoom_x,zoom_y);

        //El offset x=61, y=10 donde tiene que ir a parar el texto "256k"
        zxvision_putpixel_no_zoom(ventana,x+61,y+10,color_final);
    }
    else {
        //Aunque no estoy usando este caso, lo dejo por si en el futuro...
        //printf("follow zoom. %d,%d,%d\n",zoom_x,zoom_y,menu_gui_zoom);
        int zx,zy;
        for (zx=0;zx<zoom_x;zx++) {
            for (zy=0;zy<zoom_y;zy++) {
                zxvision_putpixel_no_zoom(ventana,(x+61)*zoom_x+zx,(y+10)*zoom_y+zy,color_final);
            }
        }

    }
}

//Variante para poder especificar un offset en la ventana
void menu_help_keyboard_overlay_render_512k_pcw(z80_byte *mem,int indice_paleta_color,zxvision_window *ventana,int x_ignore,int follow_zoom,
    int ancho_mostrar,int indice_color_transparente,int color_final_transparente)
{

    screen_render_bmpfile_function(mem,indice_paleta_color,ventana,x_ignore,follow_zoom,
        ancho_mostrar,indice_color_transparente,color_final_transparente,
        menu_help_keyboard_overlay_render_512k_pcw_putpixel);

}


void menu_help_keyboard_overlay(void)
{


    if (!si_complete_video_driver() ) return;


    //Cargar bmp si ha cambiado de maquina
    if (help_keyboard_last_current_machine!=current_machine_type) {
        help_keyboard_last_current_machine=current_machine_type;

        debug_printf(VERBOSE_DEBUG,"Loading help keyboard bmp");
        menu_help_keyboard_load_bmp();
    }

	//Si no hay archivo bmp cargado
	if (help_keyboard_bmp_file_mem==NULL) return;

	menu_speech_set_tecla_pulsada(); //Si no, envia continuamente todo ese texto a speech

    //si ventana minimizada, no ejecutar todo el codigo de overlay
    if (menu_help_keyboard_overlay_window->is_minimized) return;

    //printf("Overlay help heyboard %d\n",contador_segundo);


	zxvision_window *ventana;

	ventana=menu_help_keyboard_overlay_window;

    //Solo redibujar cuando se ha refrescado el fondo de texto
    if (ventana->has_been_drawn_contents || menu_help_keyboard_overlay_force_draw) {



        //esto hara ejecutar esto 5 veces por segundo (lo habitual en muchos de estos que no actualizan siempre es 2 veces por segundo)
        if ( ((contador_segundo%200) == 0 && help_keyboard_valor_contador_segundo_anterior!=contador_segundo) ||
             menu_multitarea==0 || menu_help_keyboard_overlay_force_draw) {

            //printf ("Refrescando keyboard help. menu_help_keyboard_overlay_force_draw=%d\n",menu_help_keyboard_overlay_force_draw);

            menu_help_keyboard_overlay_force_draw=0;

                                        help_keyboard_valor_contador_segundo_anterior=contador_segundo;
            //printf ("Refrescando keyboard help. contador_segundo=%d\n",contador_segundo);


            //zoom_x de offset para evitar parpadeo con la linea del recuadro por la izquierda
            //El teclado no sigue el zoom de ventana, pero sí que sigue el zoom de gui
            screen_render_bmpfile(help_keyboard_bmp_file_mem,BMP_SECOND_INDEX_FIRST_COLOR,ventana,zoom_x,0,0,-1,0);

            //Sobreescribimos el texto "512k" encima
            if (MACHINE_IS_PCW_8512) {
                if (help_keyboard_text_512_pcw!=NULL) {
                    menu_help_keyboard_overlay_render_512k_pcw(help_keyboard_text_512_pcw,BMP_SECOND_INDEX_FIRST_COLOR,ventana,zoom_x,0,0,-1,0);
                }
            }

            ventana->has_been_drawn_contents=0;

            //Si hay que iluminar una tecla donde esta el raton

            if (menu_help_keyboard_highlight_key) {
                //printf("Iluminando %d\n",contador_segundo_infinito);
                int color=6;

                int x1=menu_help_keyboard_highlight_key_x1;
                int x2=menu_help_keyboard_highlight_key_x2;
                int y1=menu_help_keyboard_highlight_key_y1;
                int y2=menu_help_keyboard_highlight_key_y2;

                menu_help_draw_rectangle_key(ventana,x1,y1,x2-x1+1,y2-y1+1,color);
            }

            int *keyboard_map_table=keyboard_help_return_map_table();

            keyboard_help_double_key *teclas_dobles=keyboard_help_return_double_keys();

            int total_columnas;
            int total_filas;
            z80_byte **ports_table=get_keyboard_map_ports_table(&total_columnas,&total_filas);

            menu_help_keyboard_show_speccy_pressed_keys(ventana,keyboard_map_table,total_columnas,total_filas,ports_table,teclas_dobles);

        }

    }








    //Siempre hará el dibujado de contenido para evitar que cuando esta en background, otra ventana por debajo escriba algo,
    //y entonces como esta no redibuja siempre, al no escribir encima, se sobreescribe este contenido con el de otra ventana
    //En ventanas que no escriben siempre su contenido, siempre deberia estar zxvision_draw_window_contents que lo haga siempre
    zxvision_draw_window_contents(ventana);



}


void menu_help_keyboard_create_window(zxvision_window *ventana,int x,int y,int ancho,int alto,int is_minimized,int is_maximized,
    int ancho_antes_minimize,int alto_antes_minimize)
{
    zxvision_new_window_gn_cim(ventana,x,y,ancho,alto,ancho-1,alto-2,"Keyboard Help","helpshowkeyboard",is_minimized,is_maximized,ancho_antes_minimize,alto_antes_minimize);

	ventana->can_be_backgrounded=1;

    //Metemos todo el contenido de la ventana con caracter transparente, para que no haya parpadeo
    //en caso de drivers xwindows por ejemplo, pues continuamente redibuja el texto (espacios) y encima el overlay
    //Al meter caracter transparente, el normal_overlay lo ignora y no dibuja ese caracter

    //ya no hace falta transparente debido al nuevo tratamiento de cache de putchar
    //zxvision_fill_window_transparent(ventana);


}

void menu_help_keyboard_if_highlight(int indice_tecla,int x1,int y1,int x2,int y2)
{
    if (indice_tecla!=menu_help_keyboard_highlight_key_indice_anterior || !menu_help_keyboard_highlight_key) {
        menu_help_keyboard_overlay_force_draw=1;
        menu_help_keyboard_highlight_key=1;
        menu_help_keyboard_highlight_key_x1=x1;
        menu_help_keyboard_highlight_key_x2=x2;
        menu_help_keyboard_highlight_key_y1=y1;
        menu_help_keyboard_highlight_key_y2=y2;
    }
}

void menu_help_keyboard_locate_speccy_pressed_keys_highlight(int keyboard_map_coords[],int total_columnas,int total_filas,
    int x,int y,keyboard_help_double_key *teclas_dobles)
{

    int fila_tecla;
    int columna_tecla;


    for (fila_tecla=0;fila_tecla<total_filas;fila_tecla++) {

        for (columna_tecla=0;columna_tecla<total_columnas;columna_tecla++) {

            int offset=fila_tecla*total_columnas+columna_tecla; //Indice a tecla

            //indice a coordenada
            offset *=4;

            int x1=keyboard_map_coords[offset];
            int y1=keyboard_map_coords[offset+1];
            int x2=keyboard_map_coords[offset+2];
            int y2=keyboard_map_coords[offset+3];
            if (x>=x1 && x<=x2 && y>=y1 && y<=y2) {

                //Si misma tecla que la anterior, no reiluminar
                //Generamos un valor indexacion de tecla unico, y teniendo en cuenta si tecla doble o simple
                //printf("fila %d columna %d\n",fila_tecla,columna_tecla);
                //Tambien redibujar si estabamos en una tecla concreta, no marcamos nada, y luego volvemos a misma tecla anterior
                int indice_tecla=fila_tecla+(columna_tecla*256);

                menu_help_keyboard_if_highlight(indice_tecla,x1,y1,x2,y2);

                menu_help_keyboard_highlight_key_indice_anterior=indice_tecla;
                return;
            }


        }

    }

    if (teclas_dobles!=NULL) {
        int i=0;

        while(teclas_dobles[i].puerto1!=NULL) {

            int x1=teclas_dobles[i].x1;
            int y1=teclas_dobles[i].y1;
            int x2=teclas_dobles[i].x2;
            int y2=teclas_dobles[i].y2;

            if (x>=x1 && x<=x2 && y>=y1 && y<=y2) {

                //Si misma tecla que la anterior, no reiluminar
                //Generamos un valor indexacion de tecla unico, y teniendo en cuenta si tecla doble o simple
                //65536 porque el bucle anterior jamas llegara ahi asi tenemos indices diferentes
                //Tambien redibujar si estabamos en una tecla concreta, no marcamos nada, y luego volvemos a misma tecla anterior
                int indice_tecla=65536+i;
                menu_help_keyboard_if_highlight(indice_tecla,x1,y1,x2,y2);

                menu_help_keyboard_highlight_key_indice_anterior=indice_tecla;

                return;
            }

            i++;
        }
    }


    //forzar redibujado del teclado para borrar restos
    menu_help_keyboard_overlay_force_draw=1;
    menu_help_keyboard_highlight_key=0;



}

void menu_help_keyboard_highlight_key_mouse(int pulsado_x,int pulsado_y)
{

    //localizar puerto

    int *keyboard_map_table=keyboard_help_return_map_table();



    keyboard_help_double_key *teclas_dobles=keyboard_help_return_double_keys();

    int total_columnas;
    int total_filas;
    get_keyboard_map_ports_table(&total_columnas,&total_filas);

    menu_help_keyboard_locate_speccy_pressed_keys_highlight(keyboard_map_table,total_columnas,total_filas,
        pulsado_x,pulsado_y,teclas_dobles);


}

void menu_help_keyboard_send_mantenidas_pressed_keys(void)
{

    keyboard_help_double_key *teclas_dobles=keyboard_help_return_double_keys();

    int total_columnas;
    int total_filas;
    z80_byte **key_map_ports=get_keyboard_map_ports_table(&total_columnas,&total_filas);

    //int indice_a_tecla_simple,indice_a_tecla_doble;
    //int es_tecla_doble;


    int fila_tecla;
    int columna_tecla;

    int indice_a_tecla=0;

    for (fila_tecla=0;fila_tecla<total_filas;fila_tecla++) {
        z80_byte *puerto=key_map_ports[fila_tecla];
        int mascara=1;
        for (columna_tecla=0;columna_tecla<total_columnas;columna_tecla++,indice_a_tecla++) {

            if (keyboard_get_teclas_mantenidas_pulsadas_simples(indice_a_tecla)) {
                menu_help_keyboard_activate_bit(puerto,mascara);
                //*puerto &=(255-mascara);
            }

            mascara=mascara<<1;
        }

    }

    if (teclas_dobles!=NULL) {
        int i=0;

        while(teclas_dobles[i].puerto1!=NULL) {


            if (keyboard_get_teclas_mantenidas_pulsadas_dobles(i)) {
                //teclas dobles siempre es tecla pulsada: bit=0
                *teclas_dobles[i].puerto1 &=(255-teclas_dobles[i].mascara1);

                if (teclas_dobles[i].puerto2!=NULL) *teclas_dobles[i].puerto2 &=(255-teclas_dobles[i].mascara2);
            }

            i++;

        }
    }

}

void menu_help_keyboard_generate_key_mouse(int pulsado_x,int pulsado_y)
{

    //localizar puerto

    int *keyboard_map_table=keyboard_help_return_map_table();

    z80_byte *puerto1;
    z80_byte mascara1;

    z80_byte *puerto2;
    z80_byte mascara2;

    keyboard_help_double_key *teclas_dobles=keyboard_help_return_double_keys();

    int total_columnas;
    int total_filas;
    z80_byte **ports_table=get_keyboard_map_ports_table(&total_columnas,&total_filas);

    int indice_a_tecla_simple,indice_a_tecla_doble;
    int es_tecla_doble;

    //Enviar teclas marcadas
    //menu_help_keyboard_send_mantenidas_pressed_keys(ports_table,total_columnas,total_filas,teclas_dobles);
    menu_help_keyboard_send_mantenidas_pressed_keys();

    menu_help_keyboard_locate_speccy_pressed_keys(keyboard_map_table,ports_table,total_columnas,total_filas,
        pulsado_x,pulsado_y,&puerto1,&mascara1,&puerto2,&mascara2,teclas_dobles,&indice_a_tecla_simple,&indice_a_tecla_doble,&es_tecla_doble);
    if (puerto1!=NULL) {

        //teclas dobles siempre es tecla pulsada: bit=0
        if (es_tecla_doble) {

            *puerto1 &=(255-mascara1);

            if (puerto2!=NULL) {
                *puerto2 &=(255-mascara2);
            }
        }
        else {
            menu_help_keyboard_activate_bit(puerto1,mascara1);

            if (puerto2!=NULL) {
                menu_help_keyboard_activate_bit(puerto2,mascara2);
            }
        }


        int salir=0;

        //Bucle variante de espera_no_tecla
        do {

            if ( !mouse_left) {
                salir=1;
            }

            else {
                //Indicar a la cpu emulada que si se leen las teclas aun con el menu abierto
                //esta variable se vuelve a cambiar en algun punto del core, por tanto la tengo que cambiar cada vez
                zxvision_keys_event_not_send_to_machine=0;
                menu_cpu_core_loop();
            }
            //printf("en bucle %d\n",contador_segundo_infinito);

        } while (!salir);


        //liberar tecla
        //*puerto1 |=mascara1;

        //if (puerto2!=NULL) *puerto2 |=mascara2;

        //reset_keyboard_ports();

        zxvision_keys_event_not_send_to_machine=1;

    }

    //Liberar tecla pulsada directamente con boton y tambien las que estan mantenidas
    reset_keyboard_ports();
}

//Retorna 1 si se ha apuntado a alguna tecla
int menu_help_keyboard_mantener_key_mouse(int pulsado_x,int pulsado_y)
{
    int pulsada_alguna=0;

    //localizar puerto

    int *keyboard_map_table=keyboard_help_return_map_table();

    z80_byte *puerto1;
    z80_byte mascara1;

    z80_byte *puerto2;
    z80_byte mascara2;

    keyboard_help_double_key *teclas_dobles=keyboard_help_return_double_keys();

    int total_columnas;
    int total_filas;
    z80_byte **ports_table=get_keyboard_map_ports_table(&total_columnas,&total_filas);

    int indice_a_tecla_simple,indice_a_tecla_doble;
    int es_tecla_doble;

    menu_help_keyboard_locate_speccy_pressed_keys(keyboard_map_table,ports_table,total_columnas,total_filas,
        pulsado_x,pulsado_y,&puerto1,&mascara1,&puerto2,&mascara2,teclas_dobles,&indice_a_tecla_simple,&indice_a_tecla_doble,&es_tecla_doble);

    //printf("indices: %d %d\n",indice_a_tecla_simple,indice_a_tecla_doble);

    if (indice_a_tecla_simple>=0) {
        int valor=keyboard_get_teclas_mantenidas_pulsadas_simples(indice_a_tecla_simple);
        valor ^=1;
        keyboard_set_teclas_mantenidas_pulsadas_simples(indice_a_tecla_simple,valor);
        pulsada_alguna=1;
    }

    if (indice_a_tecla_doble>=0) {
        int valor=keyboard_get_teclas_mantenidas_pulsadas_dobles(indice_a_tecla_doble);
        valor ^=1;
        keyboard_set_teclas_mantenidas_pulsadas_dobles(indice_a_tecla_doble,valor);
        pulsada_alguna=1;
    }

    return pulsada_alguna;

}

zxvision_window menu_help_show_keyboard_ventana;

//Debug para mostrar coordenada al pulsar, sirve para definir nuevos teclados, de tal manera que:
//Pulsar boton izquierdo en esquina superior izquierda
//Pulsar boton izquierdo en esquina inferior derecha
//Pulsar boton derecho para salto de linea (habitualmente para indicar nueva fila en la tabla de teclas)
int keyboard_help_debug_coords=0;

int menu_help_show_keyboard_debug_ultimo_click_x=0;
int menu_help_show_keyboard_debug_ultimo_click_y=0;

void help_keyboard_start_generate_key_mouse_click(zxvision_window *ventana)
{
    //Nota: si mientras se pulsa con raton, tambien se pulsa en el teclado fisico, la tecla también se enviará
    //porque en menu_help_keyboard_generate_key_mouse se esta dentro de menu_cpu_core_loop
    //printf ("inicio pulsar raton: %d\n",contador_segundo_infinito);
    menu_help_keyboard_overlay_force_draw=1;
    int pulsado_x,pulsado_y;
    zxvision_get_mouse_in_window(ventana,&pulsado_x,&pulsado_y);

    pulsado_x *=zoom_x;
    pulsado_y *=zoom_y;

    if (menu_help_show_keyboard_debug_ultimo_click_x!=pulsado_x && menu_help_show_keyboard_debug_ultimo_click_y!=pulsado_y) {
        //Debug para mostrar coordenada pulsada
        if (keyboard_help_debug_coords) {
            printf("%d,%d,",pulsado_x,pulsado_y);
            fflush(stdout);
        }


        menu_help_show_keyboard_debug_ultimo_click_x=pulsado_x;
        menu_help_show_keyboard_debug_ultimo_click_y=pulsado_y;
    }

    menu_help_keyboard_generate_key_mouse(pulsado_x,pulsado_y);
    //printf ("despues pulsar raton: %d\n",contador_segundo_infinito);

}

void menu_help_show_keyboard(MENU_ITEM_PARAMETERS)
{



	menu_espera_no_tecla();
	menu_reset_counters_tecla_repeticion();

	if (!menu_multitarea) {
			menu_warn_message("This window needs multitask enabled");
			return;
	}

    //resetear tabla de teclas pulsadas
    keyboard_help_reset_teclas_pulsadas();

	zxvision_window *ventana;

	ventana=&menu_help_show_keyboard_ventana;

    //IMPORTANTE! no crear ventana si ya existe. Esto hay que hacerlo en todas las ventanas que permiten background.
    //si no se hiciera, se crearia la misma ventana, y en la lista de ventanas activas , al redibujarse,
    //la primera ventana repetida apuntaria a la segunda, que es el mismo puntero, y redibujaria la misma, y se quedaria en bucle colgado
    //zxvision_delete_window_if_exists(ventana);

    //Crear ventana si no existe
    if (!zxvision_if_window_already_exists(ventana)) {


        int x,y,ancho,alto,is_minimized,is_maximized,ancho_antes_minimize,alto_antes_minimize;

        if (!util_find_window_geometry("helpshowkeyboard",&x,&y,&ancho,&alto,&is_minimized,&is_maximized,&ancho_antes_minimize,&alto_antes_minimize)) {
            //x=menu_origin_x();


            //540x201 es lo que ocupa el bmp de spectrum 48k

            ancho=1+1+540/menu_char_width/zoom_x;

            alto=1+2+201/8/zoom_y;

            //printf ("ancho %d alto %d\n",ancho,alto);

            x=menu_center_x_from_width(ancho);
            y=menu_center_y()-alto/2;

        }



        menu_help_keyboard_create_window(ventana,x,y,ancho,alto,is_minimized,is_maximized,ancho_antes_minimize,alto_antes_minimize);

    }

    //Si ya existe, activar esta ventana
    else {

        zxvision_activate_this_window(ventana);
    }

    zxvision_draw_window(ventana);

    int ancho_anterior,alto_anterior;
    zxvision_window_save_size(ventana,&ancho_anterior,&alto_anterior);



    menu_help_keyboard_overlay_window=ventana; //Decimos que el overlay lo hace sobre la ventana que tenemos aqui


    help_keyboard_valor_contador_segundo_anterior=contador_segundo;

    //Cambiamos funcion overlay de texto de menu
    //Se establece a la de funcion de onda + texto

    //cambio overlay
    zxvision_set_window_overlay(ventana,menu_help_keyboard_overlay);

    //Toda ventana que este listada en zxvision_known_window_names_array debe permitir poder salir desde aqui
    //Se sale despues de haber inicializado overlay y de cualquier otra variable que necesite el overlay
    if (zxvision_currently_restoring_windows_on_start) {
        //printf ("Saliendo de ventana ya que la estamos restaurando en startup\n");
        return;
    }

	//Al entrar siempre cargar bmp, esto especialmente es importante por si conmutamos a ventana about o cualquier otra que cargue bmp,
    //que luego al entrar aqui recargue la paleta asociada
    menu_help_keyboard_load_bmp();

	z80_byte tecla;

    //para habilitar mostrar coordenadas al pulsar, para meterlo en mapa de teclas
    //clic izquierdo: muestra posicion
    //clic derecho: salto linea
    //int keyboard_debug_mouse=1;

    menu_help_show_keyboard_debug_ultimo_click_x=0;
    menu_help_show_keyboard_debug_ultimo_click_y=0;

	do {
        //printf("bucle\n");
        tecla=zxvision_common_getkey_refresh_noesperanotec_todasteclas();
        z80_byte todos_puertos_teclado_acumulado=menu_da_todas_teclas_cualquiera();
        zxvision_handle_cursors_pgupdn(ventana,tecla);
        //printf ("tecla: %d acumulado: %d\n",tecla,todos_puertos_teclado_acumulado);

        //teclas como la de la derecha de L en Z88 (;:) no tiene valor asignado de "tecla" por tanto retorna 0
        //pero la lectura de menu_da_todas_teclas_cualquiera si que indica que una tecla se ha pulsado

        //TODO: si se marcan teclas para pulsar con boton derecho,
        //solo sirven para enviar con boton izquierdo
        //no valen para aqui, no podemos generar teclas pulsadas con boton derecho
        //y leer teclado, porque entonces no sabremos cuando el usuario dejara de apretar esa tecla en el teclado,
        //porque habran todas las otras teclas que se han activado con el boton derecho y que no se liberan
        //No seria facil saber que tecla (o teclas) ha liberado el usuario si hay todas las otras que se activaron con boton derecho
        //Hay un truco para poder enviar las teclas marcadas para pulsar junto con la tecla pulsada:
        //-dejar pulsada tecla en teclado
        //-dejar pulsado boton izquierdo raton. esto tambien envia las teclas marcadas
        //-al liberar boton izquierdo, libera todas las teclas

        //TODO: si se quieren pulsar teclas con teclado y con raton, primero hay que pulsar boton raton  y mantener,
        //luego pulsar en el teclado la tecla.
        //No se puede empezar pulsando tecla en el teclado y luego con raton, que querriamos que usase este "if" de abajo,
        //porque cuando se deja de pulsar el tecla se liberan todos los puertos y entonces se pararia de enviar pulsaciones de raton

        if ((tecla || todos_puertos_teclado_acumulado!=255) && !mouse_left && !mouse_right) {
            //printf("generar tecla en condicion de tecla pulsada. tecla=%d mouse_left: %d mouse_right: %d todos_puertos_teclado_acumulado=%d\n",
            //    tecla,mouse_left,mouse_right,todos_puertos_teclado_acumulado);
            //printf("puertos en generar tecla. puerto_65278: %d puerto_32766: %d\n",puerto_65278,puerto_32766);
            z80_byte acumulado;
            int salir=0;
            menu_help_keyboard_overlay_force_draw=1;

            //printf("Inicio envio tecla\n");


            //Bucle variante de espera_no_tecla
            do {
                acumulado=menu_da_todas_teclas_cualquiera();
                if ( (acumulado & MENU_PUERTO_TECLADO_NINGUNA) == MENU_PUERTO_TECLADO_NINGUNA) {
                    salir=1;
                }

                else {
                    //Indicar a la cpu emulada que si se leen las teclas aun con el menu abierto
                    //esta variable se vuelve a cambiar en algun punto del core, por tanto la tengo que cambiar cada vez
                    zxvision_keys_event_not_send_to_machine=0;
                    menu_cpu_core_loop();
                }

                //if (mouse_left) printf("pulsado boton izquierdo: %d\n",contador_segundo_infinito);

                //Si mientras se pulsa una tecla, se hace click con raton
                if (mouse_left && si_menu_mouse_en_ventana_no_en_scrolls() && !mouse_is_dragging) {
                    help_keyboard_start_generate_key_mouse_click(ventana);
                }



            //if (contador_segundo_infinito % 1000<100) printf ("menu_espera_no_tecla acumulado: %d\n",acumulado);

            } while (!salir);
            zxvision_keys_event_not_send_to_machine=1;


            //printf("fin generar tecla\n");

        }

        //Boton izquierdo raton: pulsar tecla
        if (mouse_left && si_menu_mouse_en_ventana_no_en_scrolls() && !mouse_is_dragging) {
            help_keyboard_start_generate_key_mouse_click(ventana);
        }

        //Boton derecho raton: mantener pulsada tecla
        if (mouse_right && si_menu_mouse_en_ventana_no_en_scrolls() ) {

            int pulsado_x,pulsado_y;
            zxvision_get_mouse_in_window(ventana,&pulsado_x,&pulsado_y);

            pulsado_x *=zoom_x;
            pulsado_y *=zoom_y;

            menu_help_keyboard_overlay_force_draw=1;

            if (!menu_help_keyboard_mantener_key_mouse(pulsado_x,pulsado_y)) {

                //Si no se ha pulsado en ninguna tecla, liberar todas
                //printf("Liberar todas teclas pulsadas\n");
                keyboard_help_reset_teclas_pulsadas();
            }

            //Debug para mostrar coordenada pulsada, salto de linea con boton derecho
            if (keyboard_help_debug_coords) {
                printf("\n");
            }

            menu_espera_no_tecla();

        }

        //Si simplemente se ha movido el raton, sin pulsar, indicar tecla
        if (!tecla && todos_puertos_teclado_acumulado==255 && !mouse_left && !mouse_right && si_menu_mouse_en_ventana_no_en_scrolls() )
        {
            //printf("ver si iluminar\n");
            int pulsado_x,pulsado_y;
            zxvision_get_mouse_in_window(ventana,&pulsado_x,&pulsado_y);

            pulsado_x *=zoom_x;
            pulsado_y *=zoom_y;
            menu_help_keyboard_highlight_key_mouse(pulsado_x,pulsado_y);
        }


		if (ventana->visible_height!=alto_anterior || ventana->visible_width!=ancho_anterior) {

            zxvision_window_save_size(ventana,&ancho_anterior,&alto_anterior);

            //Esto evita el parpadeo al redimensionar para hacer mas grande. Llenamos toda la ventana con el transparente,
            //que se hace asi siempre al crearla por primera vez

            //ya no hace falta transparente debido al nuevo tratamiento de cache de putchar
            //zxvision_fill_window_transparent(ventana);

		}
	} while (tecla!=2 && tecla!=3);

	//Gestionar salir con tecla background

	menu_espera_no_tecla(); //Si no, se va al menu anterior.
	//En AY Piano por ejemplo esto no pasa aunque el estilo del menu es el mismo...



	//Grabar geometria ventana
	util_add_window_geometry_compact(ventana);


	if (tecla==3) {
		zxvision_message_put_window_background();
	}

	else {
		zxvision_destroy_window(ventana);
		free(help_keyboard_bmp_file_mem);
 	}


}


void menu_ql_microdrive_floppy(MENU_ITEM_PARAMETERS)
{
	ql_microdrive_floppy_emulation ^=1;
}



int menu_storage_ql_mdv_flp(char *string_root_dir,enum ql_qdos_unidades unidad)
{

    char *filtros[2];

    filtros[0]="";
    filtros[1]=0;


    //guardamos directorio actual
    char directorio_actual[PATH_MAX];
    getcwd(directorio_actual,PATH_MAX);

    int ret;


    char nada[PATH_MAX];

    //Obtenemos ultimo directorio visitado
    zvfs_chdir(string_root_dir);


    ret=menu_filesel("Enter dir & press ESC",filtros,nada);


    //Si sale con ESC
    if (ret==0) {

        debug_printf (VERBOSE_DEBUG,"Selected directory: %s",menu_filesel_last_directory_seen);

        ql_insert_mdv_flp(unidad,menu_filesel_last_directory_seen);


    }

    //volvemos a directorio inicial
    zvfs_chdir(directorio_actual);

    return ret;


}


void menu_ql_mdv1(MENU_ITEM_PARAMETERS)
{
	menu_storage_ql_mdv_flp(ql_mdv1_root_dir,QL_QDOS_UNIT_MDV1);

}

void menu_ql_mdv2(MENU_ITEM_PARAMETERS)
{
	menu_storage_ql_mdv_flp(ql_mdv2_root_dir,QL_QDOS_UNIT_MDV2);
}

void menu_ql_flp1(MENU_ITEM_PARAMETERS)
{
	menu_storage_ql_mdv_flp(ql_flp1_root_dir,QL_QDOS_UNIT_FLP1);
}


/*
void menu_ql_replace_underscore(MENU_ITEM_PARAMETERS)
{
	ql_replace_underscore_dot.v ^=1;
}


void menu_ql_replace_underscore_only_one(MENU_ITEM_PARAMETERS)
{
	ql_replace_underscore_dot_only_one.v ^=1;
}
*/


void menu_ql_flp1_follow_mdv1(MENU_ITEM_PARAMETERS)
{
	ql_flp1_follow_mdv1.v ^=1;

    ql_copy_if_flp1_follow_mdv1();
}

void menu_ql_data_size_headerless(MENU_ITEM_PARAMETERS)
{
    char string_data_size[7];


    sprintf (string_data_size,"%d",ql_task_default_data_size);



    int retorno=menu_ventana_scanf_numero("Data size",string_data_size,7,+4096,0,131072,1);
    if (retorno>=0) {
        int valor=parse_string_to_number(string_data_size);

        if (valor<0 || valor>131072) debug_printf(VERBOSE_ERR,"Invalid value. Must be in range (0-131072)");
        else ql_task_default_data_size=valor;
    }
}

void menu_ql_mdv1_readonly(MENU_ITEM_PARAMETERS)
{
    ql_device_mdv1_readonly ^= 1;
}

void menu_ql_mdv2_readonly(MENU_ITEM_PARAMETERS)
{
    ql_device_mdv2_readonly ^= 1;
}

void menu_ql_flp1_readonly(MENU_ITEM_PARAMETERS)
{
    ql_device_flp1_readonly ^= 1;
}

void menu_ql_mdv1_enable(MENU_ITEM_PARAMETERS)
{
    ql_device_mdv1_enabled ^=1;
}

void menu_ql_mdv2_enable(MENU_ITEM_PARAMETERS)
{
    ql_device_mdv2_enabled ^=1;
}

void menu_ql_flp1_enable(MENU_ITEM_PARAMETERS)
{
    ql_device_flp1_enabled ^=1;
}

int menu_ql_mdv1_emulation_cond(void)
{
    if (ql_mdv1_root_dir[0]==0) return 0;
    else return 1;
}

int menu_ql_mdv2_emulation_cond(void)
{
    if (ql_mdv2_root_dir[0]==0) return 0;
    else return 1;
}

int menu_ql_flp1_emulation_cond(void)
{
    if (ql_flp1_root_dir[0]==0) return 0;
    else return 1;
}

void menu_ql_win1_alias_mdv1(MENU_ITEM_PARAMETERS)
{
    ql_win1_alias_mdv1.v ^=1;
}

void menu_ql_mdv_flp(MENU_ITEM_PARAMETERS)
{

    //Dado que es una variable local, siempre podemos usar este nombre array_menu_common
    menu_item *array_menu_common;
    menu_item item_seleccionado;
    int retorno_menu;
    do {


        menu_add_item_menu_inicial_format(&array_menu_common,MENU_OPCION_NORMAL,menu_ql_microdrive_floppy,NULL,"[%c] Microdrive & Floppy",
                (ql_microdrive_floppy_emulation ? 'X' : ' ') );
        menu_add_item_menu_tooltip(array_menu_common,"Enable Microdrive & Floppy emulation");
        menu_add_item_menu_ayuda(array_menu_common,"Enable Microdrive & Floppy emulation");

        if (ql_microdrive_floppy_emulation) {
            menu_add_item_menu_separator(array_menu_common);

            char string_ql_mdv1_root_dir_shown[28];
            char string_ql_mdv2_root_dir_shown[28];
            char string_ql_flp1_root_dir_shown[28];
            menu_tape_settings_trunc_name(ql_mdv1_root_dir,string_ql_mdv1_root_dir_shown,28);
            menu_tape_settings_trunc_name(ql_mdv2_root_dir,string_ql_mdv2_root_dir_shown,28);
            menu_tape_settings_trunc_name(ql_flp1_root_dir,string_ql_flp1_root_dir_shown,28);

            menu_add_item_menu_format(array_menu_common,MENU_OPCION_SEPARADOR,NULL,NULL,"Mdv1 root dir:");
            menu_add_item_menu_prefijo(array_menu_common,"    ");

            menu_add_item_menu_format(array_menu_common,MENU_OPCION_NORMAL,menu_ql_mdv1,NULL,"[%s]",string_ql_mdv1_root_dir_shown);
            menu_add_item_menu_prefijo(array_menu_common,"    ");
            menu_add_item_menu_tooltip(array_menu_common,"Directory used for mdv1 emulation");
            menu_add_item_menu_ayuda(array_menu_common,"Directory used for mdv1 emulation");

            menu_add_item_menu_en_es_ca(array_menu_common,MENU_OPCION_NORMAL,menu_ql_mdv1_enable,menu_ql_mdv1_emulation_cond,
                "Enabled","Activado","Activat");
            menu_add_item_menu_prefijo_format(array_menu_common,"[%c] ",(ql_device_mdv1_enabled ? 'X' : ' ') );
            menu_add_item_menu_tooltip(array_menu_common,"Enable mdv1 emulation");
            menu_add_item_menu_ayuda(array_menu_common,"Enable mdv1 emulation");


            menu_add_item_menu_format(array_menu_common,MENU_OPCION_NORMAL,menu_ql_mdv1_readonly,NULL,"[%c] Read only",
                (ql_device_mdv1_readonly ? 'X' : ' ') );
            menu_add_item_menu_tooltip(array_menu_common,"Only allow read operations on mdv1, not writing");
            menu_add_item_menu_ayuda(array_menu_common,"Only allow read operations on mdv1, not writing");

            menu_add_item_menu_format(array_menu_common,MENU_OPCION_NORMAL,menu_ql_win1_alias_mdv1,NULL,"[%c] win1 alias mdv1",
                (ql_win1_alias_mdv1.v ? 'X' : ' ') );
            menu_add_item_menu_tooltip(array_menu_common,"If win1_ device is the same as mdv1_");
            menu_add_item_menu_ayuda(array_menu_common,"If win1_ device is the same as mdv1_. win1_ device is used on QL-SD, "
                "which is not emulated by ZEsarUX, but useful enabling this for Basic programs that use this device name");

            menu_add_item_menu_separator(array_menu_common);


            menu_add_item_menu_format(array_menu_common,MENU_OPCION_SEPARADOR,NULL,NULL,"Mdv2 root dir:");
            menu_add_item_menu_prefijo(array_menu_common,"    ");

            menu_add_item_menu_format(array_menu_common,MENU_OPCION_NORMAL,menu_ql_mdv2,NULL,"[%s]",string_ql_mdv2_root_dir_shown);
            menu_add_item_menu_prefijo(array_menu_common,"    ");
            menu_add_item_menu_tooltip(array_menu_common,"Directory used for mdv2 emulation");
            menu_add_item_menu_ayuda(array_menu_common,"Directory used for mdv2 emulation");

            menu_add_item_menu_en_es_ca(array_menu_common,MENU_OPCION_NORMAL,menu_ql_mdv2_enable,menu_ql_mdv2_emulation_cond,
                "Enabled","Activado","Activat");
            menu_add_item_menu_prefijo_format(array_menu_common,"[%c] ",(ql_device_mdv2_enabled ? 'X' : ' ') );
            menu_add_item_menu_tooltip(array_menu_common,"Enable mdv2 emulation");
            menu_add_item_menu_ayuda(array_menu_common,"Enable mdv2 emulation");


            menu_add_item_menu_format(array_menu_common,MENU_OPCION_NORMAL,menu_ql_mdv2_readonly,NULL,"[%c] Read only",
                (ql_device_mdv2_readonly ? 'X' : ' ') );
            menu_add_item_menu_tooltip(array_menu_common,"Only allow read operations on mdv2, not writing");
            menu_add_item_menu_ayuda(array_menu_common,"Only allow read operations on mdv2, not writing");

            menu_add_item_menu_separator(array_menu_common);

            menu_add_item_menu_format(array_menu_common,MENU_OPCION_SEPARADOR,NULL,NULL,"Flp1 root dir:");
            menu_add_item_menu_prefijo(array_menu_common,"    ");

            menu_add_item_menu_format(array_menu_common,MENU_OPCION_NORMAL,menu_ql_flp1,NULL,"[%s]",string_ql_flp1_root_dir_shown);
            menu_add_item_menu_prefijo(array_menu_common,"    ");
            menu_add_item_menu_tooltip(array_menu_common,"Directory used for flp1 emulation");
            menu_add_item_menu_ayuda(array_menu_common,"Directory used for flp1 emulation");

            menu_add_item_menu_en_es_ca(array_menu_common,MENU_OPCION_NORMAL,menu_ql_flp1_enable,menu_ql_flp1_emulation_cond,
                "Enabled","Activado","Activat");
            menu_add_item_menu_prefijo_format(array_menu_common,"[%c] ",(ql_device_flp1_enabled ? 'X' : ' ') );
            menu_add_item_menu_tooltip(array_menu_common,"Enable flp1 emulation");
            menu_add_item_menu_ayuda(array_menu_common,"Enable flp1 emulation");


            menu_add_item_menu_format(array_menu_common,MENU_OPCION_NORMAL,menu_ql_flp1_readonly,NULL,"[%c] Read only",
                (ql_device_flp1_readonly ? 'X' : ' ') );
            menu_add_item_menu_tooltip(array_menu_common,"Only allow read operations on flp1, not writing");
            menu_add_item_menu_ayuda(array_menu_common,"Only allow read operations on flp1, not writing");

            menu_add_item_menu_separator(array_menu_common);


            /*
            menu_add_item_menu_format(array_menu_common,MENU_OPCION_NORMAL,menu_ql_replace_underscore,NULL,"[%c] Replace _ to . in filename",
                (ql_replace_underscore_dot.v ? 'X' : ' ') );

            if (ql_replace_underscore_dot.v) {
                menu_add_item_menu_format(array_menu_common,MENU_OPCION_NORMAL,menu_ql_replace_underscore_only_one,NULL,"[%c] Replace only the extension",
                    (ql_replace_underscore_dot_only_one.v ? 'X' : ' ') );
            }
            */


            menu_add_item_menu_format(array_menu_common,MENU_OPCION_NORMAL,menu_ql_flp1_follow_mdv1,NULL,"[%c] FLP1 path follows MDV1",
                (ql_flp1_follow_mdv1.v ? 'X' : ' ') );
            menu_add_item_menu_tooltip(array_menu_common,"flp1 path parameter is copied from mdv1 path parameter");
            menu_add_item_menu_ayuda(array_menu_common,"flp1 path parameter is copied from mdv1 path parameter");

            menu_add_item_menu_format(array_menu_common,MENU_OPCION_NORMAL,menu_ql_data_size_headerless,NULL,"Data size for headerless exe [%6d]",
                ql_task_default_data_size);
            menu_add_item_menu_prefijo(array_menu_common,"    ");
            menu_add_item_menu_tooltip(array_menu_common,"Assign this data  size for headerless exe files");
            menu_add_item_menu_ayuda(array_menu_common,"Assign this data  size for headerless exe files");

        }


        menu_add_item_menu_separator(array_menu_common);

        menu_add_ESC_item(array_menu_common);

        retorno_menu=menu_dibuja_menu_no_title_lang(&ql_mdv_flp_opcion_seleccionada,&item_seleccionado,array_menu_common,"Microdrive & Floppy" );


        if ((item_seleccionado.tipo_opcion&MENU_OPCION_ESC)==0 && retorno_menu>=0) {
                //llamamos por valor de funcion
                if (item_seleccionado.menu_funcion!=NULL) {
                        //printf ("actuamos por funcion\n");
                        item_seleccionado.menu_funcion(item_seleccionado.valor_opcion);

                }
        }

    } while ( (item_seleccionado.tipo_opcion&MENU_OPCION_ESC)==0 && retorno_menu!=MENU_RETORNO_ESC && !salir_todos_menus);


}


zxvision_window *menu_debug_unnamed_console_overlay_window;


int menu_debug_unnamed_console_indicador_actividad_contador=0;

int menu_debug_unnamed_console_indicador_actividad_visible=0;


void menu_debug_unnamed_console_show_legend(zxvision_window *ventana)
{
        //Forzar a mostrar atajos
        z80_bit antes_menu_writing_inverse_color;
        antes_menu_writing_inverse_color.v=menu_writing_inverse_color.v;
        menu_writing_inverse_color.v=1;

        //Y linea leyenda
        char buffer_leyenda[32];
        sprintf(buffer_leyenda,"[%d] Verbose ~~level",verbose_level);
        zxvision_print_string_defaults_fillspc(ventana,1,0,buffer_leyenda);

        //Restaurar comportamiento atajos
        menu_writing_inverse_color.v=antes_menu_writing_inverse_color.v;
}

void menu_debug_unnamed_console_overlay(void)
{


    //si ventana minimizada, no ejecutar todo el codigo de overlay
    if (menu_debug_unnamed_console_overlay_window->is_minimized) return; //Sustituir xxxx_overlay_window por lo que convenga

    //printf("Overlay verbose console %d\n",contador_segundo);


    zxvision_window *ventana;

    ventana=menu_debug_unnamed_console_overlay_window;


    //Revisar aqui tambien si no esta inicializado el puntero,
    //por si esta la ventana en background y a alguien le da por desactivar el debug console
    if (debug_unnamed_console_memory_pointer==NULL || debug_unnamed_console_enabled.v==0) {
        zxvision_print_string_defaults_fillspc(ventana,1,0,"Debug console is not enabled. Enable it on Settings->Debug");
        zxvision_draw_window_contents(ventana);
        return;
    }


    int refrescar_borrado_contador=0;

    //Ver si hay que borrar contador actividad
    if (menu_debug_unnamed_console_indicador_actividad_visible>0) {
        menu_debug_unnamed_console_indicador_actividad_visible--;
        if (menu_debug_unnamed_console_indicador_actividad_visible==0) {
            //Borrar contador actividad
            //printf("---------BORRAR contador actividad\n");
            zxvision_print_string_defaults_fillspc(ventana,1,1,"");

            refrescar_borrado_contador=1;
        }
    }

    if (!debug_unnamed_console_refresh) {
        //Si no hay mensajes, ver si hay que refrescar porque se ha borrado el contador de actividad
        if (refrescar_borrado_contador) {
            //printf("---Refrescar antes de salir sin escribir\n");
            zxvision_draw_window_contents(ventana);
        }
        return;
    }


    int x,y;
    char *puntero;

    puntero=debug_unnamed_console_memory_pointer;


    for (y=0;y<DEBUG_UNNAMED_CONSOLE_HEIGHT;y++) {
        char buffer_linea[DEBUG_UNNAMED_CONSOLE_LIMIT_WIDTH+1];
        for (x=0;x<DEBUG_UNNAMED_CONSOLE_LIMIT_WIDTH;x++) {
            //printf("%c",*puntero);

            //Empieza en x+1 para dejar 1 caracter margen izquierda
            //zxvision_print_char_defaults(ventana,x+1,y+2,*puntero);

            //Hay que guardarlo como string para poder visualizar caracteres utf-8 etc
            buffer_linea[x]=*puntero;


            puntero++;
        }
        buffer_linea[x]=0;
        //Empieza en x+1 para dejar 1 caracter margen izquierda
        zxvision_print_string_defaults(ventana,1,y+2,buffer_linea);
        //printf("\n");
    }


    if (debug_unnamed_console_new_messages) {
        debug_unnamed_console_new_messages=0;
    //Mostrar indicador actividad. Para que diga que hay mensajes nuevos
    //mantener durante 50 frames (1 segundo visible) despues de ultimo mensaje
    menu_debug_unnamed_console_indicador_actividad_visible=50;
    char *mensaje="|/-\\";

    int max=strlen(mensaje);
    char mensaje_dest[32];

    int pos=menu_debug_unnamed_console_indicador_actividad_contador % max;
    menu_debug_unnamed_console_indicador_actividad_contador++;

    sprintf(mensaje_dest,"[New messages %c]",mensaje[pos]);

    zxvision_print_string_defaults_fillspc(ventana,1,1,mensaje_dest);

    }

    //Mostar la leyenda tambien aqui, para cuando refresca en segundo plano,
    //porque a veces se redibujan las ventanas pero solo se llama al overlay, y no a la funcion principal
    menu_debug_unnamed_console_show_legend(ventana);

    zxvision_draw_window_contents(ventana);

    //Decir que no se ha modificado
    debug_unnamed_console_refresh=0;

}


void menu_debug_unnamed_console_update_size(zxvision_window *ventana)
{
            //actualizar variable del ancho para que las funciones de debug se enteren
        int ancho_leido=ventana->visible_width-2;

        //Controlar maximo
        if (ancho_leido>DEBUG_UNNAMED_CONSOLE_LIMIT_WIDTH) {
            ancho_leido=DEBUG_UNNAMED_CONSOLE_LIMIT_WIDTH;
        }

        //Y un minimo
        if (ancho_leido<5) ancho_leido=5;

        ancho_ventana_unnamed_console=ancho_leido;
}

zxvision_window zxvision_window_unnamed_console;

void menu_debug_unnamed_console(MENU_ITEM_PARAMETERS)
{
    /*if (!menu_multitarea) {
            menu_warn_message("This window needs multitask enabled");
            return;
    }*/




    zxvision_window *ventana;
    ventana=&zxvision_window_unnamed_console;

    //IMPORTANTE! no crear ventana si ya existe. Esto hay que hacerlo en todas las ventanas que permiten background.
    //si no se hiciera, se crearia la misma ventana, y en la lista de ventanas activas , al redibujarse,
    //la primera ventana repetida apuntaria a la segunda, que es el mismo puntero, y redibujaria la misma, y se quedaria en bucle colgado
    //zxvision_delete_window_if_exists(ventana);

    //Crear ventana si no existe
    if (!zxvision_if_window_already_exists(ventana)) {

        int x,y,ancho,alto,is_minimized,is_maximized,ancho_antes_minimize,alto_antes_minimize;

        if (!util_find_window_geometry("debugconsole",&x,&y,&ancho,&alto,&is_minimized,&is_maximized,&ancho_antes_minimize,&alto_antes_minimize)) {
            //x=menu_origin_x();
            //y=0;
            ancho=DEBUG_UNNAMED_CONSOLE_VISIBLE_INITIAL_WIDTH; //32;
            alto=18;

            x=menu_center_x()-ancho/2;
            y=menu_center_y()-alto/2;
        }

        //DEBUG_UNNAMED_CONSOLE_HEIGHT+2 porque hay dos lineas de leyenda superior
        //DEBUG_UNNAMED_CONSOLE_LIMIT_WIDTH+1 porque damos 1 espacio con margen por la izquierda
        //zxvision_new_window(ventana,x,y,ancho,alto,DEBUG_UNNAMED_CONSOLE_LIMIT_WIDTH+1,DEBUG_UNNAMED_CONSOLE_HEIGHT+2,"Debug console");

        zxvision_new_window_gn_cim(ventana,x,y,ancho,alto,DEBUG_UNNAMED_CONSOLE_LIMIT_WIDTH+1,DEBUG_UNNAMED_CONSOLE_HEIGHT+2,"Debug console","debugconsole",
            is_minimized,is_maximized,ancho_antes_minimize,alto_antes_minimize);

        //Ajustar el scroll al maximo, para entrar y mostrar las ultimas lineas

        //Con esto llegara mas alla del limite
        //dado que debug_unnamed_console_current_y, si esta al maximo, es mas de lo que se puede bajar

        int linea_scroll=debug_unnamed_console_current_y;

        //-4 para asegurarnos que siempre vaya por debajo
        linea_scroll -=(alto-4);
        if (linea_scroll<0) linea_scroll=0;
        zxvision_set_offset_y_or_maximum(ventana,linea_scroll);



        ventana->can_be_backgrounded=1;
        ventana->upper_margin=2;
        //Permitir hotkeys desde raton
        ventana->can_mouse_send_hotkeys=1;

        //indicar nombre del grabado de geometria
        //strcpy(ventana->geometry_name,"debugconsole");
        //restaurar estado minimizado de ventana
        //ventana->is_minimized=is_minimized;

    }

    //Si ya existe, activar esta ventana
    else {

        zxvision_activate_this_window(ventana);
    }

    zxvision_draw_window(ventana);

    menu_debug_unnamed_console_overlay_window=ventana; //Decimos que el overlay lo hace sobre la ventana que tenemos aqui


    //Cambiamos funcion overlay de texto de menu
    //Se establece a la de funcion de onda + texto

    //cambio overlay
    zxvision_set_window_overlay(ventana,menu_debug_unnamed_console_overlay);

    //actualizar tamaño ancho para que la funcion de debug sepa el tamaño real, incluso cuando solo entramos aqui al restaurar ventana
    menu_debug_unnamed_console_update_size(ventana);

    //Toda ventana que este listada en zxvision_known_window_names_array debe permitir poder salir desde aqui
    //Se sale despues de haber inicializado overlay y de cualquier otra variable que necesite el overlay
    if (zxvision_currently_restoring_windows_on_start) {
        //printf ("Saliendo de ventana ya que la estamos restaurando en startup\n");
        return;
    }

    menu_first_aid("debug_console");

    //Cada vez que se reentra en ventana, refrescar
    debug_unnamed_console_refresh=1;

    z80_byte tecla;
    do {
        //actualizar variable del ancho para que las funciones de debug se enteren
        menu_debug_unnamed_console_update_size(ventana);


        //printf("Ancho: %d\n",ancho_ventana_unnamed_console);

        menu_debug_unnamed_console_show_legend(ventana);


        tecla=zxvision_common_getkey_refresh();
        zxvision_handle_cursors_pgupdn(ventana,tecla);

        if (tecla=='l') {
            menu_debug_verbose(0);
            debug_unnamed_console_refresh=1;
        }

        //printf ("tecla: %d\n",tecla);
    } while (tecla!=2 && tecla!=3);




    //Grabar geometria ventana
    util_add_window_geometry_compact(ventana);

	if (tecla==3) {
		//zxvision_ay_registers_overlay
		zxvision_message_put_window_background();
	}

	else {
		zxvision_destroy_window(ventana);
 	}
}


zxvision_window *menu_audio_general_sound_overlay_window;

//Valores previos para vu meters dac
int menu_audio_general_sound_previos_dac[4];

//Valores previos para vu meters volumenes
int menu_audio_general_sound_previos_volumes[4];

//Valores previos para vu meters left right
int menu_audio_general_sound_previo_left;
int menu_audio_general_sound_previo_right;


int menu_audio_general_sound_contador_segundo_anterior;

void menu_audio_general_sound_overlay(void)
{



    //si ventana minimizada, no ejecutar todo el codigo de overlay
    if (menu_audio_general_sound_overlay_window->is_minimized) return;

    //printf("overlay general sound %d\n",contador_segundo);


    zxvision_window *ventana;

    ventana=menu_audio_general_sound_overlay_window;


    if (gs_enabled.v==0) {
        zxvision_print_string_defaults_fillspc(ventana,1,0,"General sound is not enabled");
        zxvision_draw_window_contents(ventana);
        return;
    }

    int decaer_volumenes=0;

    //esto hara ejecutar esto 2 veces por segundo
    if ( ((contador_segundo%500) == 0 && menu_audio_general_sound_contador_segundo_anterior!=contador_segundo) || menu_multitarea==0) {

        menu_audio_general_sound_contador_segundo_anterior=contador_segundo;

        decaer_volumenes=1;
    }



    int linea=0;
    char buffer_linea[64];

    //Forzar a mostrar atajos
    z80_bit antes_menu_writing_inverse_color;
    antes_menu_writing_inverse_color.v=menu_writing_inverse_color.v;
    menu_writing_inverse_color.v=1;


    sprintf(buffer_linea,"~~Mode: %s",(gs_stereo_mode.v ? "Stereo" : "Mono") );
    zxvision_print_string_defaults_fillspc(ventana,1,linea++,buffer_linea);

    //Restaurar comportamiento atajos
    menu_writing_inverse_color.v=antes_menu_writing_inverse_color.v;

    sprintf(buffer_linea,"Command Register: %02XH",gs_command_register);
    zxvision_print_string_defaults_fillspc(ventana,1,linea++,buffer_linea);

    sprintf(buffer_linea,"Status Register:  %02XH",gs_state_register);
    zxvision_print_string_defaults_fillspc(ventana,1,linea++,buffer_linea);

    sprintf(buffer_linea,"Data Register:    %02XH",gs_data_register);
    zxvision_print_string_defaults_fillspc(ventana,1,linea++,buffer_linea);

    sprintf(buffer_linea,"Output Register:  %02XH",gs_output_register);
    zxvision_print_string_defaults_fillspc(ventana,1,linea++,buffer_linea);

    sprintf(buffer_linea,"MMU Register:     %02XH",gs_memory_mapping_value);
    zxvision_print_string_defaults_fillspc(ventana,1,linea++,buffer_linea);

    zxvision_print_string_defaults_fillspc(ventana,1,linea++,"");

    int i;

    /*
    for (i=0;i<4;i++) {
        sprintf(buffer_linea,"Volume   #%d:      %02XH",i,gs_volumes[i]);
        zxvision_print_string_defaults_fillspc(ventana,1,linea++,buffer_linea);
    }
    */

    /*
    for (i=0;i<4;i++) {
        sprintf(buffer_linea,"Last DAC #%d:      %02XH",i,gs_dac_channels[i]);
        zxvision_print_string_defaults_fillspc(ventana,1,linea++,buffer_linea);
    }
    */

    //Controlar limites, dado que las variables entran sin inicializar
    for (i=0;i<4;i++) {
        if (menu_audio_general_sound_previos_dac[i]>15) menu_audio_general_sound_previos_dac[i]=15;
    }

    for (i=0;i<4;i++) {
        if (menu_audio_general_sound_previos_volumes[i]>15) menu_audio_general_sound_previos_volumes[i]=15;
    }

    if (menu_audio_general_sound_previo_left>15) menu_audio_general_sound_previo_left=15;
    if (menu_audio_general_sound_previo_right>15) menu_audio_general_sound_previo_right=15;


    int nivel_actual;
    char buf_nivel[33];

    for (i=0;i<4;i++) {
        //VU meters para volumes

        nivel_actual=gs_volumes[i];


        //Y pasar de escala 0..3F a escala 0..15
        nivel_actual /=4;

        if (nivel_actual>=16) nivel_actual=15;

        menu_audio_general_sound_previos_volumes[i]=menu_decae_ajusta_valor_volumen(menu_audio_general_sound_previos_volumes[i],nivel_actual);



        menu_string_volumen(buf_nivel,nivel_actual,menu_audio_general_sound_previos_volumes[i]);
        sprintf (buffer_linea,"Volume #%d: %02XH %s",i,gs_volumes[i],buf_nivel);
        zxvision_print_string_defaults_fillspc(ventana,1,linea++,buffer_linea);

        if (decaer_volumenes) {
            menu_audio_general_sound_previos_volumes[i]=menu_decae_dec_valor_volumen(menu_audio_general_sound_previos_volumes[i],nivel_actual);
        }




        //VU meters para DAC, para volumes


        //Valor unsigned con 0 en 128
        nivel_actual=gs_dac_channels[i];
        nivel_actual=nivel_actual-128;

        //Valor absoluto
        if (nivel_actual<0) nivel_actual=-nivel_actual;

        //Y pasar de escala 0..128 a escala 0..15
        nivel_actual /=8;

        if (nivel_actual>=16) nivel_actual=15;

        menu_audio_general_sound_previos_dac[i]=menu_decae_ajusta_valor_volumen(menu_audio_general_sound_previos_dac[i],nivel_actual);


        //char buf_nivel[33];


        menu_string_volumen(buf_nivel,nivel_actual,menu_audio_general_sound_previos_dac[i]);
        sprintf (buffer_linea,"DAC    #%d: %02XH %s",i,gs_dac_channels[i],buf_nivel);
        zxvision_print_string_defaults_fillspc(ventana,1,linea++,buffer_linea);

        if (decaer_volumenes) {
            //printf("Decaer volumen %d\n",i);
            //printf("decae actual %d\n",menu_audio_general_sound_previos_dac[i]);
            menu_audio_general_sound_previos_dac[i]=menu_decae_dec_valor_volumen(menu_audio_general_sound_previos_dac[i],nivel_actual);
        }
    }



        //VU meters para Left


        //Valor unsigned con 0 en 128
        nivel_actual=gs_dac_valor_final_left;
        nivel_actual=nivel_actual-128;

        //Valor absoluto
        if (nivel_actual<0) nivel_actual=-nivel_actual;

        //Y pasar de escala 0..128 a escala 0..15
        nivel_actual /=8;

        if (nivel_actual>=16) nivel_actual=15;

        menu_audio_general_sound_previo_left=menu_decae_ajusta_valor_volumen(menu_audio_general_sound_previo_left,nivel_actual);


        //char buf_nivel[33];


        menu_string_volumen(buf_nivel,nivel_actual,menu_audio_general_sound_previo_left);
        sprintf (buffer_linea,"Left Out:  %02XH %s",gs_dac_valor_final_left,buf_nivel);
        zxvision_print_string_defaults_fillspc(ventana,1,linea++,buffer_linea);

        if (decaer_volumenes) {
            menu_audio_general_sound_previo_left=menu_decae_dec_valor_volumen(menu_audio_general_sound_previo_left,nivel_actual);
        }

        //VU meters para Right


        //Valor unsigned con 0 en 128
        nivel_actual=gs_dac_valor_final_right;
        nivel_actual=nivel_actual-128;

        //Valor absoluto
        if (nivel_actual<0) nivel_actual=-nivel_actual;

        //Y pasar de escala 0..128 a escala 0..15
        nivel_actual /=8;

        if (nivel_actual>=16) nivel_actual=15;

        menu_audio_general_sound_previo_right=menu_decae_ajusta_valor_volumen(menu_audio_general_sound_previo_right,nivel_actual);


        //char buf_nivel[33];


        menu_string_volumen(buf_nivel,nivel_actual,menu_audio_general_sound_previo_right);
        sprintf (buffer_linea,"Right Out: %02XH %s",gs_dac_valor_final_right,buf_nivel);
        zxvision_print_string_defaults_fillspc(ventana,1,linea++,buffer_linea);

        if (decaer_volumenes) {
            menu_audio_general_sound_previo_right=menu_decae_dec_valor_volumen(menu_audio_general_sound_previo_right,nivel_actual);
        }


    //Left & Right Channels
    /*
    sprintf(buffer_linea,"Left:    %02XH",gs_dac_valor_final_left);
    zxvision_print_string_defaults_fillspc(ventana,1,linea++,buffer_linea);

    sprintf(buffer_linea,"Right:    %02XH",gs_dac_valor_final_left);
    zxvision_print_string_defaults_fillspc(ventana,1,linea++,buffer_linea);
    */

   zxvision_print_string_defaults_fillspc(ventana,1,linea++,"");

    sprintf(buffer_linea,"PC=%04X SP=%04X AF=%04X HL=%04X",
        general_sound_z80_cpu.r_pc,general_sound_z80_cpu.r_sp,general_sound_z80_cpu.r_af,general_sound_z80_cpu.r_hl);
    zxvision_print_string_defaults_fillspc(ventana,1,linea++,buffer_linea);

    sprintf(buffer_linea,"BC=%04X DE=%04X IX=%04X IY=%04X",
        general_sound_z80_cpu.r_bc,general_sound_z80_cpu.r_de,general_sound_z80_cpu.r_ix,general_sound_z80_cpu.r_iy);
    zxvision_print_string_defaults_fillspc(ventana,1,linea++,buffer_linea);





    zxvision_draw_window_contents(ventana);


}


zxvision_window zxvision_window_general_sound;

void menu_audio_general_sound(MENU_ITEM_PARAMETERS)
{
    /*if (!menu_multitarea) {
            menu_warn_message("This window needs multitask enabled");
            return;
    }*/




    zxvision_window *ventana;
    ventana=&zxvision_window_general_sound;

    //IMPORTANTE! no crear ventana si ya existe. Esto hay que hacerlo en todas las ventanas que permiten background.
    //si no se hiciera, se crearia la misma ventana, y en la lista de ventanas activas , al redibujarse,
    //la primera ventana repetida apuntaria a la segunda, que es el mismo puntero, y redibujaria la misma, y se quedaria en bucle colgado
    //zxvision_delete_window_if_exists(ventana);

    //Crear ventana si no existe
    if (!zxvision_if_window_already_exists(ventana)) {

        int x,y,ancho,alto,is_minimized,is_maximized,ancho_antes_minimize,alto_antes_minimize;

        if (!util_find_window_geometry("audiogensound",&x,&y,&ancho,&alto,&is_minimized,&is_maximized,&ancho_antes_minimize,&alto_antes_minimize)) {
            //x=menu_origin_x();
            //y=1;
            ancho=33;
            alto=22;

            x=menu_center_x()-ancho/2;
            y=menu_center_y()-alto/2;
        }


        //Crear nueva ventana, asignando ademas geometry name y gestionando si se crea minimizada
        zxvision_new_window_gn_cim(ventana,x,y,ancho,alto,ancho-1,alto-2,"General sound","audiogensound",is_minimized,is_maximized,ancho_antes_minimize,alto_antes_minimize);


        //zxvision_new_window(ventana,x,y,ancho,alto,ancho-1,alto-2,"General sound");

        ventana->can_be_backgrounded=1;
        //ventana->upper_margin=2;
        //Permitir hotkeys desde raton
        ventana->can_mouse_send_hotkeys=1;

        //indicar nombre del grabado de geometria
        //strcpy(ventana->geometry_name,"audiogensound");
        //restaurar estado minimizado de ventana
        //ventana->is_minimized=is_minimized;

    }

    //Si ya existe, activar esta ventana
    else {

        zxvision_activate_this_window(ventana);
    }

    zxvision_draw_window(ventana);

    menu_audio_general_sound_overlay_window=ventana; //Decimos que el overlay lo hace sobre la ventana que tenemos aqui


    //Cambiamos funcion overlay de texto de menu
    //Se establece a la de funcion de onda + texto

    //cambio overlay
    zxvision_set_window_overlay(ventana,menu_audio_general_sound_overlay);

    //Toda ventana que este listada en zxvision_known_window_names_array debe permitir poder salir desde aqui
    //Se sale despues de haber inicializado overlay y de cualquier otra variable que necesite el overlay
    if (zxvision_currently_restoring_windows_on_start) {
        //printf ("Saliendo de ventana ya que la estamos restaurando en startup\n");
        return;
    }

    z80_byte tecla;
    do {


        tecla=zxvision_common_getkey_refresh();
        zxvision_handle_cursors_pgupdn(ventana,tecla);

        if (tecla=='m') {
            gs_stereo_mode.v ^=1;
        }


        //printf ("tecla: %d\n",tecla);
    } while (tecla!=2 && tecla!=3);




    //Grabar geometria ventana
    util_add_window_geometry_compact(ventana);

	if (tecla==3) {
		//zxvision_ay_registers_overlay
		zxvision_message_put_window_background();
	}

	else {
		zxvision_destroy_window(ventana);
 	}
}






zxvision_window *menu_debug_ioports_overlay_window;

//Ultimo valor de lineas totales obtenido desde overlay
//Inicializado con algo por si acaso
int total_lineas_menu_debug_ioports=10;
int antes_total_lineas_menu_debug_ioports=10;


//texto que contiene cada linea con ajuste de palabra. Al trocear las lineas aumentan
char debug_io_ports_buffer_lineas[MAX_LINEAS_TOTAL_GENERIC_MESSAGE][MAX_ANCHO_LINEAS_GENERIC_MESSAGE];

//Punteros a cada linea de esas
char *debug_ioports_punteros_lineas[MAX_LINEAS_TOTAL_GENERIC_MESSAGE];

//Obtener lineas de debug y separar cada linea
void menu_debug_ioports_overlay_get_lines(void)
{


    //Inicializar punteros a lineas
    int i;
    for (i=0;i<MAX_LINEAS_TOTAL_GENERIC_MESSAGE;i++) {
        debug_ioports_punteros_lineas[i]=&debug_io_ports_buffer_lineas[i][0];
    }

    char *io_buffer=util_malloc_max_texto_generic_message("Can not allocate memory for io buffer");


	debug_get_ioports(io_buffer);

    //printf("io ports: %s\n",io_buffer);


    total_lineas_menu_debug_ioports=zxvision_trocear_string_lineas(io_buffer,debug_ioports_punteros_lineas);

    free(io_buffer);
}


void menu_debug_ioports_overlay(void)
{



    menu_speech_set_tecla_pulsada(); //Si no, envia continuamente todo ese texto a speech

    //si ventana minimizada, no ejecutar todo el codigo de overlay
    if (menu_debug_ioports_overlay_window->is_minimized) return;


    zxvision_window *ventana;

    ventana=menu_debug_ioports_overlay_window;



    //Obtener lineas de debug y separar cada linea
    menu_debug_ioports_overlay_get_lines();

    //Si necesita aumentar total height de ventana
    if (total_lineas_menu_debug_ioports>antes_total_lineas_menu_debug_ioports) {
        debug_printf(VERBOSE_DEBUG,"Need to enlarge Debug I/O ports window total height to %d",total_lineas_menu_debug_ioports);
        zxvision_set_total_height(ventana,total_lineas_menu_debug_ioports);
    }

    //Si ha cambiado (tanto para mayor como menor), borrar contenido para rellenarlo de nuevo
    if (total_lineas_menu_debug_ioports!=antes_total_lineas_menu_debug_ioports) {
        zxvision_cls(ventana);
    }


    antes_total_lineas_menu_debug_ioports=total_lineas_menu_debug_ioports;

    int i;




    //Escribir cada linea
    //printf("total lineas: %d\n",total_lineas);

    for (i=0;i<total_lineas_menu_debug_ioports;i++) {
        zxvision_print_string_defaults_fillspc(ventana,1,i,debug_io_ports_buffer_lineas[i]);
    }


    zxvision_draw_window_contents(ventana);


}


zxvision_window zxvision_window_debug_ioports;

void menu_debug_ioports(MENU_ITEM_PARAMETERS)
{
    /*if (!menu_multitarea) {
            menu_warn_message("This window needs multitask enabled");
            return;
    }*/




    zxvision_window *ventana;
    ventana=&zxvision_window_debug_ioports;

    //IMPORTANTE! no crear ventana si ya existe. Esto hay que hacerlo en todas las ventanas que permiten background.
    //si no se hiciera, se crearia la misma ventana, y en la lista de ventanas activas , al redibujarse,
    //la primera ventana repetida apuntaria a la segunda, que es el mismo puntero, y redibujaria la misma, y se quedaria en bucle colgado
    //zxvision_delete_window_if_exists(ventana);


    //Crear ventana si no existe
    if (!zxvision_if_window_already_exists(ventana)) {

        int x,y,ancho,alto,is_minimized,is_maximized,ancho_antes_minimize,alto_antes_minimize;

        if (!util_find_window_geometry("debugioports",&x,&y,&ancho,&alto,&is_minimized,&is_maximized,&ancho_antes_minimize,&alto_antes_minimize)) {
            //x=menu_origin_x();
            //y=1;
            ancho=33;
            alto=22;

            x=menu_center_x()-ancho/2;
            y=menu_center_y()-alto/2;
        }


        char titulo_ventana[64];

        if (CPU_IS_MOTOROLA) strcpy(titulo_ventana,"IO Addresses");
        else strcpy(titulo_ventana,"IO Ports");

        int total_alto=alto-2;

        //Calculo de minimo alto total, segun las lineas
        //Esta ventana tiene la particularidad que no sabemos a priori cuanto ocupara en alto total
        //dependera de la cantidad de lineas de i/o ports
        //Y o bien lo calculamos aqui al empezar, cosa que implicaria meter codigo redundante de la funcion de overlay,
        //o bien obtenemos el ultimo valor que muestra la funcion de overlay
        //Si lo obtenemos de overlay, implica que siempre habra que entrar una primera vez, con tamaño quiza menor,
        //y luego volver a entrar para que la ventana se recree
        //Mejor es reusar codigo para calcular ese alto

        //llamamos una primera vez para obtener total de lineas
        menu_debug_ioports_overlay_get_lines();

        if (total_alto<total_lineas_menu_debug_ioports) {
            total_alto=total_lineas_menu_debug_ioports;
            debug_printf(VERBOSE_DEBUG,"Increasing total Debug I/O window height to: %d",total_alto);
        }

        //zxvision_new_window(ventana,x,y,ancho,alto,ancho-1,total_alto,titulo_ventana);


        zxvision_new_window_gn_cim(ventana,x,y,ancho,alto,ancho-1,total_alto,titulo_ventana,"debugioports",is_minimized,is_maximized,ancho_antes_minimize,alto_antes_minimize);

        ventana->can_be_backgrounded=1;


        //indicar nombre del grabado de geometria
        //strcpy(ventana->geometry_name,"debugioports");

        //restaurar estado minimizado de ventana
        //ventana->is_minimized=is_minimized;

    }

    //Si ya existe, activar esta ventana
    else {
        zxvision_activate_this_window(ventana);
    }

    zxvision_draw_window(ventana);

    menu_debug_ioports_overlay_window=ventana; //Decimos que el overlay lo hace sobre la ventana que tenemos aqui


    //Cambiamos funcion overlay de texto de menu
    //Se establece a la de funcion de onda + texto

    //cambio overlay
    zxvision_set_window_overlay(ventana,menu_debug_ioports_overlay);

    //Toda ventana que este listada en zxvision_known_window_names_array debe permitir poder salir desde aqui
    //Se sale despues de haber inicializado overlay y de cualquier otra variable que necesite el overlay
    if (zxvision_currently_restoring_windows_on_start) {
        //printf ("Saliendo de ventana ya que la estamos restaurando en startup\n");
        return;
    }

    z80_byte tecla;
    do {


        tecla=zxvision_common_getkey_refresh();
        zxvision_handle_cursors_pgupdn(ventana,tecla);


        //printf ("tecla: %d\n",tecla);
    } while (tecla!=2 && tecla!=3);




    //Grabar geometria ventana
    util_add_window_geometry_compact(ventana);

	if (tecla==3) {
		//zxvision_ay_registers_overlay
		zxvision_message_put_window_background();
	}

	else {
		zxvision_destroy_window(ventana);
 	}
}



zxvision_window *menu_new_about_window_overlay_window;


//z80_byte *new_about_window_bmp_file_mem=NULL;

int new_about_window_ancho_mostrar=0;

//Dice el about logo activo
//Por defecto la 1 (aunque en version 11 es la 2, la de David)
//0 salamanquesa primera
//1 salamanquesa segunda
//2 David
//3 Diego
int cual_about_logo=1;

unsigned char *retorna_bitmap_salamanquesa(void)
{
    if (cual_about_logo==1) return bitmap_salamanquesa_otra_mas;
    else if (cual_about_logo==2) return david_pequenyo_64pix_transparent;
    else if (cual_about_logo==3) return diego_64pix_transparent;
    else return bitmap_salamanquesa;
}

int retorna_color_transparente_salamanquesa(void)
{

    if (cual_about_logo==1) return 0xfe;
    else if (cual_about_logo==2) return 255;
    else if (cual_about_logo==3) return 0x23;
    else return 255;

}

void menu_new_about_window_overlay(void)
{


    if (!si_complete_video_driver() ) return;



    menu_speech_set_tecla_pulsada(); //Si no, envia continuamente todo ese texto a speech

	zxvision_window *ventana;

	ventana=menu_new_about_window_overlay_window;


    //No redibujar si no hay cambios de nada
    if (!ventana->dirty_user_must_draw_contents) return;

    ventana->dirty_user_must_draw_contents=0;

    //printf("Redibujando about %d\n",contador_segundo);

    //Dibujar siempre la imagen, a 50 fps


    //zoom_x de offset para evitar parpadeo con la linea del recuadro por la izquierda
    //screen_render_bmpfile(new_about_window_bmp_file_mem,BMP_INDEX_FIRST_COLOR,ventana,zoom_x,1);

    //parametro x_ignore a 1 para evitar dibujar x=0 que sobreescribiria el marco izquierdo, produciendo parpadeo

    //aun asi, la imagen es de 63 pixeles de ancho, porque me producia parpadeo en la derecha, cosa que no entiendo,
    //podria tener una imagen de 64 pixeles de ancho y no caer la imagen 1 pixel sobre el texto de la derecha


    //mostrarla solo si hay archivo cargado. podria ser que alguien borrase el bmp
	//if (new_about_window_bmp_file_mem!=NULL) {
        int color_transparente=retorna_color_transparente_salamanquesa();

        screen_render_bmpfile(retorna_bitmap_salamanquesa(),BMP_INDEX_FIRST_COLOR,ventana,1,1,new_about_window_ancho_mostrar,color_transparente,ESTILO_GUI_PAPEL_NORMAL);
    //}



    //Siempre hará el dibujado de contenido para evitar que cuando esta en background, otra ventana por debajo escriba algo,
    //y entonces como esta no redibuja siempre, al no escribir encima, se sobreescribe este contenido con el de otra ventana
    //En ventanas que no escriben siempre su contenido, siempre deberia estar zxvision_draw_window_contents que lo haga siempre
    zxvision_draw_window_contents(ventana);



}


void menu_about_load_bmp_palette(void)
{
    util_bmp_load_palette(retorna_bitmap_salamanquesa(),BMP_INDEX_FIRST_COLOR);
}


zxvision_window menu_about_new_ventana;

void menu_about_new(MENU_ITEM_PARAMETERS)
{



	menu_espera_no_tecla();
	menu_reset_counters_tecla_repeticion();


	zxvision_window *ventana;

	ventana=&menu_about_new_ventana;



	int x_ventana,y_ventana,ancho_ventana,alto_ventana;

    //hardcoded. la imagen bmp hace 64x64
    int ancho_imagen_salamanquesa=64;
    int alto_imagen_salamanquesa=64;


    int x_texto=ancho_imagen_salamanquesa/menu_char_width;
    //printf("x_texto: %d\n",x_texto);
    //con esto, son 8 caracteres cuando el menu_char_width=8 por defecto
    //pero si son menos de 7, pasaria que el texto se sobreescribe la primera columna por la imagen. ajustar

    //if (menu_char_width!=8) x_texto++;

    //printf("x_texto ajustado: %d\n",x_texto);
    //12345678901234567890123456789
    //(C) 2013 Cesar Hernandez Bano"
    // - Toi Acid Game edition - "

    //Lo siguiente es para que: con menu char width distinto de 8, quedaria un hueco vertical "transparente" entre el logo y el texto
    //esto seria casi imposible de evitar, pues ubico la zona del logo con transparente, y ese logo es multiple de 8 el ancho
    //la solucion es mostrar el logo siempre a multiplo del char width, por ejemplo si char width es 7,
    //mostrar 63 de ancho (que es multiple de 7). Y eso requiere la funcion de screen_render_bmpfile se le pueda
    //cambiar el ancho

    //Realmente lo que hago es cortar un poco el logo por la derecha pero como tiene margen en blanco, no pasa nada

    int ancho_mostrar_bmp=64/menu_char_width;

    int final_ancho_mostrar_bmp=ancho_mostrar_bmp*menu_char_width;

    //E indicamos a la funcion de render bmp, desde el overlay, el ancho final
    //printf("ancho bmp: %d\n",final_ancho_mostrar_bmp);

    new_about_window_ancho_mostrar=final_ancho_mostrar_bmp;

    //Textos. Creamos antes para ver el que tiene mas ancho
    char mensaje_about[3][200];
    //unsigned char letra_enye;

    //mensaje completo con enye en segundo apellido
    //letra_enye=129;

    sprintf (mensaje_about[0],"ZEsarUX " EMULATOR_VERSION " (" EMULATOR_SHORT_DATE ")");
    sprintf (mensaje_about[1]," - " EMULATOR_EDITION_NAME " - ");
    //sprintf (mensaje_about[2],"(C) 2013 Cesar Hernandez Ba%co",letra_enye);
    sprintf (mensaje_about[2],"(C) 2013 César Hernández Bañó");
    //sprintf (mensaje_about[2],"(C) 2013 Cesar Hernandez Bano");

    int ancho_maximo=0;
    int i;

    for (i=0;i<3;i++) {
        int ancho_texto=strlen(mensaje_about[i]);

        //TODO: dado que en mi nombre he puesto 3 acentos y una eñe, son 4 caracteres utf, que ocupan 2 bytes cada uno
        //por tanto strlen ve aparentemente 4 bytes de mas, los restamos para que se redimensione correctamente la ventana
        //lo ideal seria tener una funcion diferente a strlen que de la longitud real de un texto considerando utf
        if (i==2) ancho_texto -=4;

        if (ancho_texto>ancho_maximo) ancho_maximo=ancho_texto;
    }

    //+1 de margen derecho. el habitual izquierdo no lo contamos pues la parte izquierda ya la ocupa el logo
    ancho_ventana=ancho_maximo+x_texto+1;


    alto_ventana=10;



    //x_ventana=menu_center_x()-ancho_ventana/2;

    //Dado que si tenemos la opcion activada de situar ventanas en zx desktop por defecto,
    //si zx desktop es muy pequeño, no cabera ahi, y entonces la ventana se redimensiona al maximo como consecuencia del error
    //de que no cabe
    //por eso en vez de obtener menu_center_x() mejor usamos scr_get_menu_width que nos da el ancho total

    x_ventana=(scr_get_menu_width()-ancho_ventana)/2;

    y_ventana=menu_center_y()-alto_ventana/2;

    //printf ("ancho %d alto %d\n",ancho,alto);



    int ancho_ventana_visible=ancho_ventana-1;
    int alto_ventana_visible=alto_ventana-2;


	zxvision_new_window(ventana,x_ventana,y_ventana,ancho_ventana,alto_ventana,
							ancho_ventana_visible,alto_ventana_visible,"About");


    //nota: la carga del juego de edicion y por tanto uno de los easter egg no sale con este about, logicamente pues no tenemos
    //opcion para buscar texto asi... hay que hacerlo desde el otro about, que se dispara si no hay zx desktop habilitado

    ventana->can_be_resized=0;
    ventana->can_be_minimized=0;

	zxvision_draw_window(ventana);


    //Cargar el archivo bmp
    /*
    bmp. 256 colour (indexed)
    */

/*
    char nombrebmp[PATH_MAX];

    strcpy(nombrebmp,"salamanquesa.bmp");


    //localizarlo
    char buffer_nombre[PATH_MAX];

    int existe=find_sharedfile(nombrebmp,buffer_nombre);
    if (!existe)  {
        debug_printf(VERBOSE_DEBUG,"Unable to find bmp file %s",nombrebmp);
        //no lanzamos error de esto, al menos mostrar ventana about
        //return;

        //decimos imagen a NULL para que no la muestre
        //en ese caso si que habra un recuadro transparente donde estaria la imagen
        //podria hacer que en este caso el recuadro no saliera, pero lo prefiero asi,
        //asi el usuario de alguna manera sabe que algo raro pasa... ;)
        new_about_window_bmp_file_mem=NULL;
    }

    else {
        new_about_window_bmp_file_mem=util_load_bmp_file(buffer_nombre);
    }


    //if (new_about_window_bmp_file_mem==NULL) return;
*/

    //La salamanquesa no se carga de disco sino que esta incrustada en el codigo. Es un BMP
    menu_about_load_bmp_palette();

    //Metemos todo el contenido de la ventana con caracter transparente, para que no haya parpadeo
    //en caso de drivers xwindows por ejemplo, pues continuamente redibuja el texto (espacios) y encima el overlay
    //Al meter caracter transparente, el normal_overlay lo ignora y no dibuja ese caracter

    //ya no hace falta transparente debido al nuevo tratamiento de cache de putchar
    //zxvision_fill_window_transparent(ventana);



    //Y la zona que sera de texto, la quitamos como transparente (inicializamos con espacios)



    int x,y;
    for (y=0;y<alto_ventana_visible;y++) {
        //zxvision_print_string_defaults_fillspc(ventana,10,i,"");
        for (x=x_texto;x<ancho_ventana_visible;x++) {
            //zxvision_print_string_defaults(ventana,10,y,"         ");
            zxvision_print_char_defaults(ventana,x,y,' ');
        }
    }


    int linea;

    //considerar 3 lineas para centrar el texto (la de build number no la contamos pues solo es en los snapshots)
    //linea inicial para que quede centrado
    linea=(alto_ventana_visible-3)/2;
    //printf("linea: %d\n",linea);



    for (i=0;i<3;i++) {
        zxvision_print_string_defaults(ventana,x_texto,linea++,mensaje_about[i]);
    }

#ifdef SNAPSHOT_VERSION
    char mensaje_about_build[200];
    sprintf (mensaje_about_build,"Build number: " BUILDNUMBER );
    zxvision_print_string_defaults(ventana,x_texto,linea++,mensaje_about_build);
#endif




    menu_new_about_window_overlay_window=ventana; //Decimos que el overlay lo hace sobre la ventana que tenemos aqui



    //Cambiamos funcion overlay de texto de menu

    //cambio overlay
    zxvision_set_window_overlay(ventana,menu_new_about_window_overlay);



    //si no, el texto no apareceria a no ser que movieramos el raton o la ventana, cuando multitask esta off
	if (!menu_multitarea) zxvision_draw_window_contents(ventana);

	z80_byte tecla;

    //llevar un temporizador de cuanto tiempo se esta pulsando en el logo
    int tiempo_pulsando_inicial=0;

	do {
		tecla=zxvision_common_getkey_refresh();
		zxvision_handle_cursors_pgupdn(ventana,tecla);
		//printf ("tecla: %d\n",tecla);
        if (mouse_is_clicking) {
            //empezamos a contar
            if (!tiempo_pulsando_inicial) {
                //printf("Start pressing\n");
                tiempo_pulsando_inicial=contador_segundo_infinito;
            }

            if (si_menu_mouse_en_ventana() ) {
                //printf("Mouse en ventana\n");
                if (menu_mouse_x<x_texto) {
                    //printf("Coordenada mouse en columna salamanquesa\n");
                    int alto_imagen_salamanquesa_chars=alto_imagen_salamanquesa/8;
                    //printf("%d\n",menu_mouse_y);
                    //y=0 es el titulo
                    if (menu_mouse_y>0 && menu_mouse_y<alto_imagen_salamanquesa_chars+1) {
                        //printf("Clicked en salamanquesa\n");

                        if (mouse_is_double_clicking) {
                            menu_about_about_load_editionamegame();
                            //simular tecla esc
                            tecla=2;
                        }

                        else {
                            if (contador_segundo_infinito-tiempo_pulsando_inicial>1000) {
                                //printf("Switch logo\n");
                                //click normal. cambiar a la otra salamanquesa
                                cual_about_logo++;
                                if (cual_about_logo>3) cual_about_logo=0;
                                menu_about_load_bmp_palette();

                                tiempo_pulsando_inicial=0;

                                //Y forzar redibujado del overlay
                                ventana->dirty_user_must_draw_contents=1;
                            }
                        }
                    }
                }

            }

        }

        else {
            //no contamos
            tiempo_pulsando_inicial=0;
            //printf("Not pressing\n");
        }

	} while (tecla!=2);


	menu_espera_no_tecla(); //Si no, se va al menu anterior.
	//En AY Piano por ejemplo esto no pasa aunque el estilo del menu es el mismo...




	zxvision_destroy_window(ventana);
	//free(new_about_window_bmp_file_mem);



}

void menu_debug_unload_source_code(MENU_ITEM_PARAMETERS)
{
    load_source_code_eject();

    menu_generic_message_splash("Unload Soure Code","OK File unloaded");
}


void menu_debug_load_source_code(MENU_ITEM_PARAMETERS)
{

    char source_load_file[PATH_MAX];

    char *filtros[3];

    filtros[0]="txt";
    filtros[1]="asm";
    filtros[2]=0;


    if (menu_filesel("Select Source File",filtros,source_load_file)==1) {
        int retorno=remote_load_source_code(source_load_file);

        if (!retorno) {
            menu_generic_message_splash("Load Soure Code","OK File loaded");
            //Y salimos de todos los menus
            salir_todos_menus=1;
        }

    }

}

void menu_debug_load_symbol_table(MENU_ITEM_PARAMETERS)
{

    char symbols_load_file[PATH_MAX];

    char *filtros[2];

    filtros[0]="sym";
    filtros[1]=0;


    if (menu_filesel("Select Symbol File",filtros,symbols_load_file)==1) {
        labels_load(symbols_load_file);


        menu_generic_message_splash("Load Symbol Table","OK File loaded");
        //Y salimos de todos los menus
        salir_todos_menus=1;


    }

}

char *menu_debug_show_symbol_table_texto_browser;
//int menu_debug_show_symbol_table_indice_buffer;


void menu_debug_show_symbol_table_recurse(labeltree *l)
{
    if (l==NULL) return;

    char buffer_texto[MAX_LABELTREE_NAME+100];

    sprintf(buffer_texto,"%s: %d\n",l->name,l->value);

    util_concat_string(menu_debug_show_symbol_table_texto_browser,buffer_texto,MAX_TEXTO_GENERIC_MESSAGE);


    menu_debug_show_symbol_table_recurse(l->left);
    menu_debug_show_symbol_table_recurse(l->right);
}

void menu_debug_symbol_table_unload(MENU_ITEM_PARAMETERS)
{
    labeltree_free(parse_string_labeltree);
    parse_string_labeltree=NULL;

    menu_generic_message_splash("Unload Symbol Table","OK Symbols Unloaded");

}


void menu_debug_show_symbol_table(MENU_ITEM_PARAMETERS)
{
    menu_debug_show_symbol_table_texto_browser=util_malloc_max_texto_browser();
	//menu_debug_show_symbol_table_indice_buffer=0;

    menu_debug_show_symbol_table_texto_browser[0]=0;

    menu_debug_show_symbol_table_recurse(parse_string_labeltree);



			//sprintf(buffer_bloque," count: %d",count);
			//menu_debug_show_symbol_table_indice_buffer +=util_add_string_newline(&texto_browser[menu_debug_show_symbol_table_indice_buffer],buffer_bloque);


//util_concat_string(menu_debug_show_symbol_table_texto_browser,buffer_temporal,MAX_TEXTO_GENERIC_MESSAGE);

	//menu_debug_show_symbol_table_texto_browser[menu_debug_show_symbol_table_indice_buffer]=0;
	zxvision_generic_message_tooltip("Symbol Table" , 1, 0 , 0, 0, 1, NULL, 1, 0, "%s", menu_debug_show_symbol_table_texto_browser);


    free(menu_debug_show_symbol_table_texto_browser);
}

void menu_snapshot_rewind_browse_select(MENU_ITEM_PARAMETERS)
{
    //Aplicar ese snapshot
    snapshot_in_ram_load(valor_opcion);

    menu_generic_message_splash("Load Snapshot","OK Snapshot loaded from RAM");
}

void menu_snapshot_rewind_browse(MENU_ITEM_PARAMETERS)
{

    if (snapshots_in_ram_total_elements==0) {
        menu_error_message("Snapshot list empty");
        return;
    }

    menu_item *array_menu_common;
    menu_item item_seleccionado;
    int retorno_menu;
    do {

        //Inicializar el ultimo a 0 siempre
        int snapshot_rewind_browse_opcion_seleccionada=0;

        menu_add_item_menu_inicial(&array_menu_common,"",MENU_OPCION_UNASSIGNED,NULL,NULL);


        int i;
        for (i=0;i<snapshots_in_ram_total_elements;i++) {

            //char buffer_entrada[100];
            int indice=snapshot_in_ram_get_element(i);



            menu_add_item_menu_format(array_menu_common,MENU_OPCION_NORMAL,menu_snapshot_rewind_browse_select,NULL,"%4d: %02d:%02d:%02d",
                i,snapshots_in_ram[indice].hora,snapshots_in_ram[indice].minuto,snapshots_in_ram[indice].segundo);

            menu_add_item_menu_valor_opcion(array_menu_common,i);
        }




        menu_add_item_menu_separator(array_menu_common);

        menu_add_ESC_item(array_menu_common);

        retorno_menu=menu_dibuja_menu_no_title_lang(&snapshot_rewind_browse_opcion_seleccionada,&item_seleccionado,array_menu_common,"Browse Snapshots");



        if ((item_seleccionado.tipo_opcion&MENU_OPCION_ESC)==0 && retorno_menu>=0) {
            //llamamos por valor de funcion
            if (item_seleccionado.menu_funcion!=NULL) {
                //printf ("actuamos por funcion\n");
                item_seleccionado.menu_funcion(item_seleccionado.valor_opcion);

            }
        }

    } while ( (item_seleccionado.tipo_opcion&MENU_OPCION_ESC)==0 && retorno_menu!=MENU_RETORNO_ESC && !salir_todos_menus);
}

void menu_snapshot_rewind_enable(MENU_ITEM_PARAMETERS)
{
    snapshot_in_ram_enabled.v ^=1;
}

void menu_snapshot_rewind_interval(MENU_ITEM_PARAMETERS)
{
    menu_ventana_scanf_numero_enhanced("Snapshot interval",&snapshot_in_ram_interval_seconds,3,+1,1,99,0);
}


void menu_snapshot_rewind_maximum(MENU_ITEM_PARAMETERS)
{
    menu_ventana_scanf_numero_enhanced("Maximum snapshots",&snapshots_in_ram_maximum,5,+1,1,MAX_TOTAL_SNAPSHOTS_IN_RAM,0);

    snapshots_in_ram_reset();

    menu_warn_message("Snapshot list has been cleared due to list resize");
}

void menu_snapshot_rewind_timer_timeout(MENU_ITEM_PARAMETERS)
{
    menu_ventana_scanf_numero_enhanced("Rewind timeout",&snapshot_in_ram_enabled_timer_timeout,3,+1,1,99,0);
}


void menu_snapshot_rewind(MENU_ITEM_PARAMETERS)
{
    menu_item *array_menu_common;
    menu_item item_seleccionado;
    int retorno_menu;
    do {



        menu_add_item_menu_inicial_format(&array_menu_common,MENU_OPCION_NORMAL,menu_snapshot_rewind_enable,NULL,"[%c] Enabled",
        (snapshot_in_ram_enabled.v ? 'X' : ' ' ));


        menu_add_item_menu_format(array_menu_common,MENU_OPCION_NORMAL,menu_snapshot_rewind_interval,NULL,"[%d] Interval (seconds)",snapshot_in_ram_interval_seconds);

        menu_add_item_menu_format(array_menu_common,MENU_OPCION_NORMAL,menu_snapshot_rewind_maximum,NULL,"[%d] Maximum snapshots",snapshots_in_ram_maximum);
        menu_add_item_menu_tooltip(array_menu_common,"Maximum snapshots to keep in memory");
        menu_add_item_menu_ayuda(array_menu_common,"Maximum snapshots to keep in memory. When reached the maximum, the oldest will be deleted");


        menu_add_item_menu_format(array_menu_common,MENU_OPCION_NORMAL,menu_snapshot_rewind_timer_timeout,NULL,"[%d] Rewind timeout (seconds)",snapshot_in_ram_enabled_timer_timeout);
        menu_add_item_menu_tooltip(array_menu_common,"After this time pressed rewind action, the rewind position is reset to current");
        menu_add_item_menu_ayuda(array_menu_common,"After this time pressed rewind action, the rewind position is reset to current");


        if (snapshot_in_ram_enabled.v) {

            menu_add_item_menu_separator(array_menu_common);
            menu_add_item_menu_format(array_menu_common,MENU_OPCION_NORMAL,menu_snapshot_rewind_browse,NULL,"Browse");

        }


        menu_add_item_menu_separator(array_menu_common);

        menu_add_ESC_item(array_menu_common);

        retorno_menu=menu_dibuja_menu(&snapshot_rewind_opcion_seleccionada,&item_seleccionado,array_menu_common,
            "Snapshots to RAM menu","Menú instantáneas a RAM","Menú instantànies a RAM");



        if ((item_seleccionado.tipo_opcion&MENU_OPCION_ESC)==0 && retorno_menu>=0) {
            //llamamos por valor de funcion
            if (item_seleccionado.menu_funcion!=NULL) {
                //printf ("actuamos por funcion\n");
                item_seleccionado.menu_funcion(item_seleccionado.valor_opcion);

            }
        }

    } while ( (item_seleccionado.tipo_opcion&MENU_OPCION_ESC)==0 && retorno_menu!=MENU_RETORNO_ESC && !salir_todos_menus);

}





int get_efectivo_tamanyo_find_buffer(void)
{
	if (MACHINE_IS_QL) return ql_mem_limit+1;
	return 65536;
}



//int menu_find_bytes_total_items=0;


//Indica si esta vacio o no; esto se usa para saber si la busqueda se hace sobre la busqueda anterior o no
int menu_find_bytes_empty=1;

unsigned char *menu_find_bytes_mem_pointer=NULL;
/* Estructura del array de busqueda:
65536 items del array. Cada item es un unsigned char
El valor indica:
0: en esa posicion de la memoria no se encuentra el byte
1: en esa posicion de la memoria si se encuentra el byte
otros valores de momento no tienen significado
*/


void menu_find_bytes_clear_results_process(void)
{
        //inicializamos array
        int i;

        for (i=0;i<get_efectivo_tamanyo_find_buffer();i++) menu_find_bytes_mem_pointer[i]=0;

        menu_find_bytes_empty=1;

}

void menu_find_bytes_clear_results(MENU_ITEM_PARAMETERS)
{
        menu_find_bytes_clear_results_process();

        menu_generic_message("Clear Results","OK. Results cleared");
}

/*
void old_delete_menu_find_bytes_view_results(MENU_ITEM_PARAMETERS)
{

        int index_find,index_buffer;

        char results_buffer[MAX_TEXTO_GENERIC_MESSAGE];

        //margen suficiente para que quepa una linea
        //direccion+salto linea+codigo 0
        char buf_linea[9];

        index_buffer=0;

        int encontrados=0;

        int salir=0;

        for (index_find=0;index_find<get_efectivo_tamanyo_find_buffer() && salir==0;index_find++) {
                if (menu_find_bytes_mem_pointer[index_find]) {
                        sprintf (buf_linea,"%d\n",index_find);
                        sprintf (&results_buffer[index_buffer],"%s\n",buf_linea);
                        index_buffer +=strlen(buf_linea);
                        encontrados++;
                }

                //controlar maximo
                //20 bytes de margen
                if (index_buffer>MAX_TEXTO_GENERIC_MESSAGE-20) {
                        debug_printf (VERBOSE_ERR,"Too many results to show. Showing only the first %d",encontrados);
                        //forzar salir
                        salir=1;
                }

        }

        results_buffer[index_buffer]=0;

        menu_generic_message("View Results",results_buffer);
}
*/

void menu_find_bytes_open_hexviewer(MENU_ITEM_PARAMETERS)
{
    menu_debug_hexdump_direccion=valor_opcion;
    menu_debug_hexdump(0);
}

void menu_find_bytes_view_results(MENU_ITEM_PARAMETERS)
{

    menu_item *array_menu_common;
    menu_item item_seleccionado;
    int retorno_menu;

    int opcion_seleccionada_common=0;
    do {

        menu_add_item_menu_inicial(&array_menu_common,"",MENU_OPCION_UNASSIGNED,NULL,NULL);



        int index_find;



        //margen suficiente para que quepa una linea
        //direccion+salto linea+codigo 0
        //char buf_linea[9];

        //index_buffer=0;

        //int encontrados=0;

        int salir=0;

        int total_items=0;

        for (index_find=0;index_find<get_efectivo_tamanyo_find_buffer() && salir==0;index_find++) {
                if (menu_find_bytes_mem_pointer[index_find]) {
                        //sprintf (buf_linea,"%d\n",index_find);
                        //sprintf (&results_buffer[index_buffer],"%s\n",buf_linea);
                        //index_buffer +=strlen(buf_linea);

                        menu_add_item_menu_format(array_menu_common,MENU_OPCION_NORMAL,menu_find_bytes_open_hexviewer,NULL,"%d",index_find);
                        menu_add_item_menu_valor_opcion(array_menu_common,index_find);
                        menu_add_item_menu_tooltip(array_menu_common,"Press Enter to open Hexadecimal Editor on this address");
                        menu_add_item_menu_ayuda(array_menu_common,"Press Enter to open Hexadecimal Editor on this address");

                        total_items++;

                }


        }

        if (total_items==0) {
            menu_add_item_menu_format(array_menu_common,MENU_OPCION_NORMAL,NULL,NULL,"Empty list");
        }

        menu_add_item_menu_separator(array_menu_common);

        menu_add_ESC_item(array_menu_common);

        retorno_menu=menu_dibuja_menu_no_title_lang(&opcion_seleccionada_common,&item_seleccionado,array_menu_common,"View results");



        if ((item_seleccionado.tipo_opcion&MENU_OPCION_ESC)==0 && retorno_menu>=0) {
            //llamamos por valor de funcion
            if (item_seleccionado.menu_funcion!=NULL) {
                //printf ("actuamos por funcion\n");
                item_seleccionado.menu_funcion(item_seleccionado.valor_opcion);

            }
        }

    } while ( (item_seleccionado.tipo_opcion&MENU_OPCION_ESC)==0 && retorno_menu!=MENU_RETORNO_ESC && !salir_todos_menus);
}

//Busqueda desde direccion indicada
int menu_find_bytes_process_from(z80_byte byte_to_find,int inicio)
{
	int dir;
	int total_items_found=0;
	int final_find=get_efectivo_tamanyo_find_buffer();

	//Busqueda con array no inicializado
	if (menu_find_bytes_empty) {

					debug_printf (VERBOSE_INFO,"Starting Search with no previous results");


					//asumimos que no va a encontrar nada
					menu_find_bytes_empty=1;

					for (dir=inicio;dir<final_find;dir++) {
									if (peek_byte_z80_moto(dir)==byte_to_find) {
													menu_find_bytes_mem_pointer[dir]=1;

													//al menos hay un resultado
													menu_find_bytes_empty=0;

													//incrementamos contador de resultados para mostrar al final
													total_items_found++;
									}
					}

	}

	else {
					//Busqueda con array ya con contenido
					//examinar solo las direcciones que indique el array

					debug_printf (VERBOSE_INFO,"Starting Search using previous results");

					//asumimos que no va a encontrar nada
					menu_find_bytes_empty=1;

					int i;
					for (i=0;i<final_find;i++) {
									if (menu_find_bytes_mem_pointer[i]) {
													//Ver el contenido de esa direccion
													if (peek_byte_z80_moto(i)==byte_to_find) {
																	//al menos hay un resultado
																	menu_find_bytes_empty=0;
																	//incrementamos contador de resultados para mostrar al final
																	total_items_found++;
													}
													else {
																	//el byte ya no esta en esa direccion
																	menu_find_bytes_mem_pointer[i]=0;
													}
									}
					}
	}

	return total_items_found;

}

//Busqueda desde direccion 0
int menu_find_bytes_process(z80_byte byte_to_find)
{
	return menu_find_bytes_process_from(byte_to_find,0);
}

//Busqueda desde direccion indicada una lista de bytes
//menu_z80_moto_int dir,int *lista,int total_items
int menu_find_bytes_list_from(int *lista,int total_items,int inicio,int case_insensitive)
{
	int dir;
	int total_items_found=0;
	int final_find=get_efectivo_tamanyo_find_buffer();

	//Busqueda con array no inicializado
	if (menu_find_bytes_empty) {

					debug_printf (VERBOSE_INFO,"Starting Search with no previous results");


					//asumimos que no va a encontrar nada
					menu_find_bytes_empty=1;

					for (dir=inicio;dir<final_find;dir++) {
                                    if (util_compare_bytes_address(dir,lista,total_items,case_insensitive)) {
									//if (peek_byte_z80_moto(dir)==byte_to_find) {
													menu_find_bytes_mem_pointer[dir]=1;

													//al menos hay un resultado
													menu_find_bytes_empty=0;

													//incrementamos contador de resultados para mostrar al final
													total_items_found++;
									}
					}

	}

	else {
					//Busqueda con array ya con contenido
					//examinar solo las direcciones que indique el array

					debug_printf (VERBOSE_INFO,"Starting Search using previous results");

					//asumimos que no va a encontrar nada
					menu_find_bytes_empty=1;

					int i;
					for (i=0;i<final_find;i++) {
									if (menu_find_bytes_mem_pointer[i]) {
													//Ver el contenido de esa direccion
                                                    if (util_compare_bytes_address(i,lista,total_items,case_insensitive)) {
													//if (peek_byte_z80_moto(i)==byte_to_find) {
																	//al menos hay un resultado
																	menu_find_bytes_empty=0;
																	//incrementamos contador de resultados para mostrar al final
																	total_items_found++;
													}
													else {
																	//el byte ya no esta en esa direccion
																	menu_find_bytes_mem_pointer[i]=0;
													}
									}
					}
	}

	return total_items_found;

}



//Para buscar mas de 1 byte separado por espacio
void menu_find_bytes_find(MENU_ITEM_PARAMETERS)
{

    //maximos bytes a buscar
    #define MAX_BYTES_FIND 30

    #define MAX_STRING_FIND_BUFFER (MAX_BYTES_FIND*4)

    int lista[MAX_BYTES_FIND];

    //Buscar en la memoria direccionable (0...65535) si se encuentran los bytes indicados
    //z80_byte byte_to_find;


    char string_find[MAX_STRING_FIND_BUFFER];

    string_find[0]=0;

    menu_ventana_scanf("Values space separated",string_find,MAX_STRING_FIND_BUFFER);

    //Si cadena vacia, no hacer nada
    if (string_find[0]==0) return;

    //ir procesando cada valor
    int i;

    //int longitud=strlen(string_find);

    //indice al string
    int indice_numero=0;

    int total_numeros=0;

    int salir=0;

    i=0;

    //for (i=0;i<longitud;i++) {
    while (!salir) {
        if (string_find[i]==0) salir=1;

        if (string_find[i]==' ' || string_find[i]==0) {

            if (total_numeros==MAX_BYTES_FIND) {
                menu_error_message("Maximum bytes in list reached");
                return;
            }

            string_find[i]=0;
            int valor_find=parse_string_to_number(&string_find[indice_numero]);

            lista[total_numeros]=valor_find;
            //printf("numero: %d\n",valor_find);

            indice_numero=i+1;
            total_numeros++;

        }

        i++;
    }



    int total_items_found;

    //total_items_found=menu_find_bytes_process(byte_to_find);

    total_items_found=menu_find_bytes_list_from(lista,total_numeros,0,0);

    menu_generic_message_format("Find","Total addresses found: %d",total_items_found);


}



//Para buscar texto
void menu_find_string(MENU_ITEM_PARAMETERS)
{

    //maximos bytes a buscar
    #define MENU_FIND_STRING_MAX 100

    int lista[MENU_FIND_STRING_MAX];

    //Buscar en la memoria direccionable (0...65535) si se encuentran los bytes indicados
    //z80_byte byte_to_find;


    char string_find[MENU_FIND_STRING_MAX];

    string_find[0]=0;

    menu_ventana_scanf("Write string",string_find,MENU_FIND_STRING_MAX);

    //Si cadena vacia, no hacer nada
    if (string_find[0]==0) return;

    //ir procesando cada valor
    int i;

    for (i=0;string_find[i];i++) {
        lista[i]=(unsigned char)string_find[i];
    }

    int total_numeros=i;


    int total_items_found;

    total_items_found=menu_find_bytes_list_from(lista,total_numeros,0,1);

    menu_generic_message_format("Find","Total addresses found: %d",total_items_found);


}


//int total_tamanyo_find_buffer=0;

void menu_find_bytes_alloc_if_needed(void)
{
	//Si puntero memoria no esta asignado, asignarlo, o si hemos cambiado de tipo de maquina
	if (menu_find_bytes_mem_pointer==NULL) {

					//65536 elementos del array, cada uno de tamanyo unsigned char
					//total_tamanyo_find_buffer=get_total_tamanyo_find_buffer();

					menu_find_bytes_mem_pointer=malloc(QL_MAXIMUM_MEM_LIMIT+1); //Asignamos el maximo (maquina QL)
					if (menu_find_bytes_mem_pointer==NULL) cpu_panic("Error allocating find array");

					//inicializamos array
					int i;
					for (i=0;i<get_efectivo_tamanyo_find_buffer();i++) menu_find_bytes_mem_pointer[i]=0;
	}
}

void menu_find_bytes(MENU_ITEM_PARAMETERS)
{
        menu_item *array_menu_find_bytes;
        menu_item item_seleccionado;
        int retorno_menu;

        //Si puntero memoria no esta asignado, asignarlo, o si hemos cambiado de tipo de maquina
        menu_find_bytes_alloc_if_needed();


        do {

            char tipo_busqueda[20];
            if (menu_find_bytes_empty) strcpy(tipo_busqueda,"(initial)");
            else strcpy(tipo_busqueda,"(next)");

                menu_add_item_menu_inicial_format(&array_menu_find_bytes,MENU_OPCION_NORMAL,menu_find_bytes_find,NULL,"Find bytes %s",tipo_busqueda);
                menu_add_item_menu_tooltip(array_menu_find_bytes,"Find several byte on memory");
                menu_add_item_menu_ayuda(array_menu_find_bytes,"Find some bytes on the 64 KB of mapped memory, considering the last address found (if any).\n"
                        "It can also be used to find POKEs, it's very easy: \n"
                        "I first recommend to disable Multitasking menu, to avoid losing lives where in the menu.\n"
                        "As an example, you are in a game with 4 lives. Enter find byte "
                        "with value 4. It will find a lot of addresses, don't panic.\n"
                        "Then, you must lose one live, you have now 3. Then you must search for byte with value 3; "
                        "the search will be made considering the last search results. Normally here you will find only "
                        "one address with value 3. At this moment you know the address where the lives are stored.\n"
                        "The following to find, for example, infinite lives, is to search where this life value is decremented. "
                        "You can find it by making a MRA breakpoint to the address where the lives are stored, or a condition breakpoint "
			"(NN)=value, setting NN to the address where lives are stored, and value to the desired number of lives, for example: (51308)=2.\n"
                        "When the breakpoint is caught, you will probably have the section of code where the lives are decremented ;) ");

                menu_add_item_menu_format(array_menu_find_bytes,MENU_OPCION_NORMAL,menu_find_string,NULL,"Find text string %s",tipo_busqueda);

                menu_add_item_menu_format(array_menu_find_bytes,MENU_OPCION_NORMAL,menu_find_bytes_view_results,NULL,"View results");
                menu_add_item_menu_tooltip(array_menu_find_bytes,"View results");
                menu_add_item_menu_ayuda(array_menu_find_bytes,"View results");

                menu_add_item_menu_format(array_menu_find_bytes,MENU_OPCION_NORMAL,menu_find_bytes_clear_results,NULL,"Clear results");
                menu_add_item_menu_tooltip(array_menu_find_bytes,"Clear results");
                menu_add_item_menu_ayuda(array_menu_find_bytes,"Clear results");




                menu_add_item_menu(array_menu_find_bytes,"",MENU_OPCION_SEPARADOR,NULL,NULL);


                menu_add_ESC_item(array_menu_find_bytes);

                retorno_menu=menu_dibuja_menu_no_title_lang(&find_bytes_opcion_seleccionada,&item_seleccionado,array_menu_find_bytes,"Find bytes" );



                if ((item_seleccionado.tipo_opcion&MENU_OPCION_ESC)==0 && retorno_menu>=0) {
                        //llamamos por valor de funcion
                        if (item_seleccionado.menu_funcion!=NULL) {
                                //printf ("actuamos por funcion\n");
                                item_seleccionado.menu_funcion(item_seleccionado.valor_opcion);

                        }
                }

        } while ( (item_seleccionado.tipo_opcion&MENU_OPCION_ESC)==0 && retorno_menu!=MENU_RETORNO_ESC && !salir_todos_menus);
}





/*
Busqueda de contador de vidas.
Estados: 0. Inicial. Se indica vidas actuales. Se realiza busqueda en toda memoria con ese valor
Estados: 1. Ya se ha indicado vidas iniciales. Se puede indicar vidas actuales. Se realiza busqueda en toda memoria. Si se encuentra 1 solo valor, se pasa a estado 2.
Se puede pasar a estado 0 con "Restart"

Estados: 2. Ya se ha encontrado contador vidas. Se muestra direccion. Se puede pasar a estado 0 con "Restart"

*/

int menu_find_lives_state=0;

//Puntero a memoria de spectrum que dice donde esta el contador de vidas
z80_int menu_find_lives_pointer=0;

//Ultimo valor buscado
int lives_to_find=3;


void menu_find_lives_restart(MENU_ITEM_PARAMETERS)
{
	menu_find_lives_state=0;
}

void menu_find_lives_initial(MENU_ITEM_PARAMETERS)
{
	//Limpiar resultados
	menu_find_bytes_alloc_if_needed();
	if (menu_find_lives_state==0) menu_find_bytes_clear_results_process();

	//Suponemos que la segunda vez habra perdido al menos 1 vida
	if (menu_find_lives_state==1 && lives_to_find>0) lives_to_find--;

	//Pedir vidas actuales
	//Buscar en la memoria direccionable (0...65535) si se encuentra el byte


	char string_find[4];

	sprintf (string_find,"%d",lives_to_find);

	menu_ventana_scanf("Current lives",string_find,4);

	int valor_find=parse_string_to_number(string_find);

	if (valor_find<0 || valor_find>255) {
					debug_printf (VERBOSE_ERR,"Invalid value %d",valor_find);
					return;
	}


	lives_to_find=valor_find;


	int total_items_found;

	//Empezar a buscar desde la 16384. No tiene sentido buscar desde la rom
	total_items_found=menu_find_bytes_process_from(lives_to_find,16384);

	//menu_generic_message_format("Find","Total addresses found: %d",total_items_found);

	//Si estamos en estado 0
	if (menu_find_lives_state==0) {
		if (total_items_found==0) {
			 menu_generic_message("Find lives","Sorry, no lives counter found");
		}
		else {
			menu_generic_message("Find lives","Ok. Continue playing game and come back when you lose a life");
			menu_find_lives_state=1;
		}
	}

	//Si estamos en estado 1
	else if (menu_find_lives_state==1) {
		if (total_items_found==0) {
			menu_generic_message("Find lives","Sorry, I haven't found any addresses. The process has been restarted");
			menu_find_lives_state=0;
		}
		else if (total_items_found!=1) {
			 menu_generic_message("Find lives","Sorry, no unique address found. You may want to try again losing another live or maybe manually find it on the Find bytes menu");
		}
		else {

			//Buscar la direccion
			int index_find;

			int salir=0;

			for (index_find=0;index_find<get_efectivo_tamanyo_find_buffer() && salir==0;index_find++) {
							if (menu_find_bytes_mem_pointer[index_find]) {
											menu_find_lives_pointer=index_find;
											salir=0;
							}
			}


			menu_find_lives_state=2;

			menu_generic_message("Find lives","Great. Memory pointer found");
		}
	}



	//Buscar bytes

}


//Ultimo valor de vidas seleccionadas
z80_byte ultimo_menu_find_lives_set=9;

void menu_find_lives_set(MENU_ITEM_PARAMETERS)
{


	char string_lives[4];

	sprintf (string_lives,"%d",ultimo_menu_find_lives_set);

	menu_ventana_scanf("Lives?",string_lives,4);

	int valor_lives=parse_string_to_number(string_lives);

	if (valor_lives<0 || valor_lives>255) {
					debug_printf (VERBOSE_ERR,"Invalid value %d",valor_lives);
					return;
	}

	ultimo_menu_find_lives_set=valor_lives;

	poke_byte_z80_moto(menu_find_lives_pointer,valor_lives);

}

void menu_find_lives(MENU_ITEM_PARAMETERS)
{
        menu_item *array_menu_find_lives;
        menu_item item_seleccionado;
        int retorno_menu;




        do {

								if (menu_find_lives_state==0) {
                	menu_add_item_menu_inicial_format(&array_menu_find_lives,MENU_OPCION_NORMAL,menu_find_lives_initial,NULL,"Tell current lives (initial)");
								}

								if (menu_find_lives_state==1) {
									menu_add_item_menu_inicial_format(&array_menu_find_lives,MENU_OPCION_NORMAL,menu_find_lives_initial,NULL,"Tell current lives (decr.)");
								}

								if (menu_find_lives_state==2) {
									menu_add_item_menu_inicial_format(&array_menu_find_lives,MENU_OPCION_NORMAL,NULL,NULL,"Lives pointer: %d",menu_find_lives_pointer);
									menu_add_item_menu_format(array_menu_find_lives,MENU_OPCION_NORMAL,NULL,NULL,"Lives: %d",peek_byte_z80_moto(menu_find_lives_pointer) );
									menu_add_item_menu_format(array_menu_find_lives,MENU_OPCION_NORMAL,menu_find_lives_set,NULL,"Set lives");
								}

								if (menu_find_lives_state==1 || menu_find_lives_state==2) {
									menu_add_item_menu_format(array_menu_find_lives,MENU_OPCION_NORMAL,menu_find_lives_restart,NULL,"Restart process");
								}

                menu_add_item_menu(array_menu_find_lives,"",MENU_OPCION_SEPARADOR,NULL,NULL);


                menu_add_ESC_item(array_menu_find_lives);

                retorno_menu=menu_dibuja_menu_no_title_lang(&find_lives_opcion_seleccionada,&item_seleccionado,array_menu_find_lives,"Find lives" );



                if ((item_seleccionado.tipo_opcion&MENU_OPCION_ESC)==0 && retorno_menu>=0) {
                        //llamamos por valor de funcion
                        if (item_seleccionado.menu_funcion!=NULL) {
                                //printf ("actuamos por funcion\n");
                                item_seleccionado.menu_funcion(item_seleccionado.valor_opcion);

                        }
                }

        } while ( (item_seleccionado.tipo_opcion&MENU_OPCION_ESC)==0 && retorno_menu!=MENU_RETORNO_ESC && !salir_todos_menus);
}

int menu_memory_cheat_array_inicializado=0;
int menu_memory_cheat_realizado_first_scan=0;

struct menu_memory_cheat_array_struct {
    z80_byte value_first_scan;
    z80_byte value_last_scan;
    int matches;
};

struct menu_memory_cheat_array_struct *menu_memory_cheat_array_list;

enum memory_cheat_first_scan_possible_conditions {
    MEMORY_CHEAT_FIRST_SCAN_EXACT_VALUE=0,
    MEMORY_CHEAT_FIRST_SCAN_BIGGER_THAN,
    MEMORY_CHEAT_FIRST_SCAN_SMALLER_THAN,
    MEMORY_CHEAT_FIRST_SCAN_VALUE_BETWEEN,
    MEMORY_CHEAT_FIRST_SCAN_UNKNOWN_VALUE
};

char *memory_cheat_first_scan_possible_conditions_strings[]={
    "Exact Value",
    "Bigger Than",
    "Smaller Than",
    "Value Between",
    "Unknown Value"
};

enum memory_cheat_first_scan_possible_conditions memory_cheat_first_scan_condition=MEMORY_CHEAT_FIRST_SCAN_UNKNOWN_VALUE;
z80_byte memory_cheat_first_scan_condition_first_parameter=0;
z80_byte memory_cheat_first_scan_condition_second_parameter=255;


enum memory_cheat_next_scan_possible_conditions {
    MEMORY_CHEAT_NEXT_SCAN_EXACT_VALUE=0,
    MEMORY_CHEAT_NEXT_SCAN_BIGGER_THAN,
    MEMORY_CHEAT_NEXT_SCAN_SMALLER_THAN,
    MEMORY_CHEAT_NEXT_SCAN_VALUE_BETWEEN,
    MEMORY_CHEAT_NEXT_SCAN_INCREASED_VALUE,
    MEMORY_CHEAT_NEXT_SCAN_INCREASED_VALUE_BY,
    MEMORY_CHEAT_NEXT_SCAN_DECREASED_VALUE,
    MEMORY_CHEAT_NEXT_SCAN_DECREASED_VALUE_BY,
    MEMORY_CHEAT_NEXT_SCAN_CHANGED_VALUE,
    MEMORY_CHEAT_NEXT_SCAN_UNCHANGED_VALUE,
    MEMORY_CHEAT_NEXT_SCAN_SAME_AS_FIRST,
};

char *memory_cheat_next_scan_possible_conditions_strings[]={
    "Exact Value",
    "Bigger Than",
    "Smaller Than",
    "Value Between",
    "Increased Value",
    "Increased Value By",
    "Decreased Value",
    "Decreased Value By",
    "Changed Value",
    "Unchanged value",
    "Same as First"
};

enum memory_cheat_next_scan_possible_conditions memory_cheat_next_scan_condition=MEMORY_CHEAT_NEXT_SCAN_EXACT_VALUE;
z80_byte memory_cheat_next_scan_condition_first_parameter=0;
z80_byte memory_cheat_next_scan_condition_second_parameter=255;

//para los watches
struct memory_cheat_watch_struct {
    int activo;
    int direccion;
};

#define MEMORY_CHEAT_MAX_WATCHES 4
struct memory_cheat_watch_struct memory_cheat_watch_list[MEMORY_CHEAT_MAX_WATCHES];
#define MEMORY_CHEAT_WATCHES_LINE 8

int memory_cheat_total_elements;

void menu_memory_cheat_init_array_malloc(void)
{
    debug_printf(VERBOSE_INFO,"Allocating memory for Memory Cheat List");

    memory_cheat_total_elements=get_efectivo_tamanyo_find_buffer();

    int total_memoria=sizeof(struct menu_memory_cheat_array_struct)*memory_cheat_total_elements;

    menu_memory_cheat_array_list=util_malloc(total_memoria,"Can not initialize results list");

    int i;

    for (i=0;i<memory_cheat_total_elements;i++) {
        menu_memory_cheat_array_list[i].matches=0;
    }
}

void menu_memory_cheat_init_array(void)
{
    if (!menu_memory_cheat_array_inicializado) {
        menu_memory_cheat_array_inicializado=1;

        menu_memory_cheat_init_array_malloc();

        //Inicializar watches
        int i;
        for (i=0;i<4;i++) {
            memory_cheat_watch_list[i].activo=0;
        }

    }

    //Si se cambia de maquina spectrum a ql, hay que aumentar el array
    if (memory_cheat_total_elements!=get_efectivo_tamanyo_find_buffer()) {
        debug_printf(VERBOSE_INFO,"Memory cheat array list must be reallocated due to machine change");

        free(menu_memory_cheat_array_list);

        menu_memory_cheat_init_array_malloc();
    }
}


void menu_memory_cheat_set_watch_or_write_no_ask_choice(int opcion,int direccion,int watch_id,int pedir_direccion_en_set_watch)
{


    //Pedir direccion
    if (direccion==-1 || (pedir_direccion_en_set_watch && opcion==2)) {
        char string_direccion[8];
        strcpy(string_direccion,"0");

        menu_ventana_scanf("Address",string_direccion,8);

        direccion=parse_string_to_number(string_direccion);
    }



    if (opcion==1) {


        //Ventana para hacer poke
        char string_valor[4];
        sprintf (string_valor,"%XH",peek_byte_z80_moto(direccion));

        char titulo_ventana[100];
        sprintf(titulo_ventana,"New Value to %XH",direccion);
        menu_ventana_scanf(titulo_ventana,string_valor,4);
        z80_byte new_value=parse_string_to_number(string_valor);

        poke_byte_z80_moto(direccion,new_value);
    }

    if (opcion==2) {

        //Pedir watch_id si es -1
        if (watch_id==-1) {
            char string_valor[4];
            strcpy(string_valor,"1");

            menu_ventana_scanf("Watch (1-4)",string_valor,4);
            watch_id=parse_string_to_number(string_valor);
            if (watch_id<1 || watch_id>MEMORY_CHEAT_MAX_WATCHES) {
                debug_printf(VERBOSE_ERR,"Invalid watch number");
            }
            watch_id--;
        }

        memory_cheat_watch_list[watch_id].activo=1;
        memory_cheat_watch_list[watch_id].direccion=direccion;
    }

}

void menu_memory_cheat_set_watch_or_write(int direccion,int watch_id,int pedir_direccion_en_set_watch)
{
    int opcion;

    if (watch_id>=0) {
        opcion=menu_simple_four_choices("Action","Do you want to","Write value","Set watch","Open Hexadecimal Editor","Remove watch");
    }

    else {
        opcion=menu_simple_three_choices("Action","Do you want to","Write value","Set watch","Open Hexadecimal Editor");
    }

    if (opcion==3) {
        menu_debug_hexdump_direccion=direccion;
        menu_debug_hexdump(0);
    }

    else if (opcion==4) {
        memory_cheat_watch_list[watch_id].activo=0;
    }

    else {

        menu_memory_cheat_set_watch_or_write_no_ask_choice(opcion,direccion,watch_id,pedir_direccion_en_set_watch);

    }

    //no queremos que al pulsar ESC se cierre la ventana de memory cheat
    salir_todos_menus=0;

}

void menu_memory_cheat_view_results(MENU_ITEM_PARAMETERS)
{

	char *texto_browser=util_malloc_max_texto_browser();
	int indice_buffer=0;


    int total_elementos=get_efectivo_tamanyo_find_buffer();

    int i;

    int salir=0;

    char buffer_linea[100];

    for (i=0;i<total_elementos && !salir;i++) {

        if (menu_memory_cheat_array_list[i].matches) {
            //printf("Adding %d\n",i);

            int digitos=4;

            if (CPU_IS_MOTOROLA) digitos=6;

            /*if (CPU_IS_MOTOROLA) {
                sprintf(buffer_linea,"%06XH, had value %02XH (%3d)",i,menu_memory_cheat_array_list[i].value_last_scan,menu_memory_cheat_array_list[i].value_last_scan);
            }
            else {
                sprintf(buffer_linea,"%04XH, had value %02XH (%3d)",i,menu_memory_cheat_array_list[i].value_last_scan,menu_memory_cheat_array_list[i].value_last_scan);
            }*/

           z80_byte valor_actual=peek_byte_z80_moto(i);

            sprintf(buffer_linea,"%0*XH, scan %02XH (%3d), now %02XH (%3d)",
                digitos,i,menu_memory_cheat_array_list[i].value_last_scan,menu_memory_cheat_array_list[i].value_last_scan,
                valor_actual,valor_actual);

            indice_buffer +=util_add_string_newline(&texto_browser[indice_buffer],buffer_linea);

            //Proteccion aqui tambien porque pueden generarse muchos bloques en este bucle
            if (indice_buffer>=MAX_TEXTO_BROWSER-1024) {
                    debug_printf(VERBOSE_ERR,"Too many entries. Showing only what is allowed on memory");
                    salir=1;
                    break;
            }


        }

    }



	texto_browser[indice_buffer]=0;


	generic_message_tooltip_return retorno_ventana;


	zxvision_generic_message_tooltip("View Results" , 1, 0 , 0, 0, 1, &retorno_ventana, 1, 0, "%s", texto_browser);


    free(texto_browser);

    //Impedir que desde menu_generic_message se pulse en otra ventana u F5 porque entonces se enviara una orden
    //de cierre al menu tabulado con OK, y no se quedaria la ventana de memory cheat en background
    salir_todos_menus=0;


	//Si se sale con ESC
    if (retorno_ventana.estado_retorno==0) return;

    //printf("Linea seleccionada: %s\n",retorno_ventana.texto_seleccionado);

    //Para saber la dirección, simplemente parseamos esa linea hasta el ","

    for (i=0;retorno_ventana.texto_seleccionado[i];i++) {
        if (retorno_ventana.texto_seleccionado[i]==',') {
            //Formato es "direccion_en_hexa = . por tanto metemos una H antes del =
            //retorno_ventana.texto_seleccionado[i-1]='H';

            retorno_ventana.texto_seleccionado[i]=0;
            break;
        }
    }

    int direccion=parse_string_to_number(retorno_ventana.texto_seleccionado);
    //printf("Dir: %d\n",direccion);

    //Si hay que hacer escritura, ya sabemos la direccion
    //Si hay que hacer un set watch, tiene que pedir el watch_id
    menu_memory_cheat_set_watch_or_write(direccion,-1,0);

}



void menu_memory_cheat_first_scan_condition(MENU_ITEM_PARAMETERS)
{


    menu_item *array_menu_common;
    menu_item item_seleccionado;
    int retorno_menu;

    int opcion_seleccionada=memory_cheat_first_scan_condition;



    do {

        menu_add_item_menu_inicial(&array_menu_common,"",MENU_OPCION_UNASSIGNED,NULL,NULL);

        int i;
        for (i=MEMORY_CHEAT_FIRST_SCAN_EXACT_VALUE;i<=MEMORY_CHEAT_FIRST_SCAN_UNKNOWN_VALUE;i++) {
            menu_add_item_menu_format(array_menu_common,MENU_OPCION_NORMAL,NULL,NULL,"%s",memory_cheat_first_scan_possible_conditions_strings[i]);
        }

        menu_add_item_menu_separator(array_menu_common);

        menu_add_ESC_item(array_menu_common);

        retorno_menu=menu_dibuja_menu_dialogo_no_title_lang(&opcion_seleccionada,&item_seleccionado,array_menu_common,"Condition");



        if ((item_seleccionado.tipo_opcion&MENU_OPCION_ESC)==0 && retorno_menu>=0) {
            //Asignar tal cual la opcion y salir
            if (opcion_seleccionada<=MEMORY_CHEAT_FIRST_SCAN_UNKNOWN_VALUE) {
                memory_cheat_first_scan_condition=opcion_seleccionada;
                return;
            }
        }

    } while ( (item_seleccionado.tipo_opcion&MENU_OPCION_ESC)==0 && retorno_menu!=MENU_RETORNO_ESC && !salir_todos_menus);

    //no queremos que al pulsar ESC se cierre la ventana de memory cheat
    salir_todos_menus=0;
}

int memory_cheat_first_scan_matches(z80_byte value)
{
    switch (memory_cheat_first_scan_condition) {
        case MEMORY_CHEAT_FIRST_SCAN_EXACT_VALUE:
            if (value==memory_cheat_first_scan_condition_first_parameter) return 1;
            else return 0;
        break;

        case MEMORY_CHEAT_FIRST_SCAN_BIGGER_THAN:
            if (value>memory_cheat_first_scan_condition_first_parameter) return 1;
            else return 0;
        break;

        case MEMORY_CHEAT_FIRST_SCAN_SMALLER_THAN:
            if (value<memory_cheat_first_scan_condition_first_parameter) return 1;
            else return 0;
        break;

        case MEMORY_CHEAT_FIRST_SCAN_VALUE_BETWEEN:
            if (value>=memory_cheat_first_scan_condition_first_parameter && value<=memory_cheat_first_scan_condition_second_parameter) return 1;
            else return 0;
        break;

        case MEMORY_CHEAT_FIRST_SCAN_UNKNOWN_VALUE:
            return 1;
        break;

        default:
            return 0;
        break;
    }

}

int memory_cheat_next_scan_matches(z80_byte value,z80_byte previous_value,z80_byte first_value)
{
    switch (memory_cheat_next_scan_condition) {
        case MEMORY_CHEAT_NEXT_SCAN_EXACT_VALUE:
            if (value==memory_cheat_next_scan_condition_first_parameter) return 1;
            else return 0;
        break;

        case MEMORY_CHEAT_NEXT_SCAN_BIGGER_THAN:
            if (value>memory_cheat_next_scan_condition_first_parameter) return 1;
            else return 0;
        break;

        case MEMORY_CHEAT_NEXT_SCAN_SMALLER_THAN:
            if (value<memory_cheat_next_scan_condition_first_parameter) return 1;
            else return 0;
        break;

        case MEMORY_CHEAT_NEXT_SCAN_VALUE_BETWEEN:
            if (value>=memory_cheat_next_scan_condition_first_parameter && value<=memory_cheat_next_scan_condition_second_parameter) return 1;
            else return 0;
        break;

        case MEMORY_CHEAT_NEXT_SCAN_INCREASED_VALUE:
            if (value>previous_value) return 1;
            else return 0;
        break;

        case MEMORY_CHEAT_NEXT_SCAN_INCREASED_VALUE_BY:
            if (value==previous_value+memory_cheat_next_scan_condition_first_parameter) return 1;
            else return 0;
        break;

        case MEMORY_CHEAT_NEXT_SCAN_DECREASED_VALUE:
            if (value<previous_value) return 1;
            else return 0;
        break;

        case MEMORY_CHEAT_NEXT_SCAN_DECREASED_VALUE_BY:
            if (value==previous_value-memory_cheat_next_scan_condition_first_parameter) return 1;
            else return 0;
        break;

        case MEMORY_CHEAT_NEXT_SCAN_CHANGED_VALUE:
            if (value!=previous_value) return 1;
            else return 0;
        break;

        case MEMORY_CHEAT_NEXT_SCAN_UNCHANGED_VALUE:
            if (value==previous_value) return 1;
            else return 0;
        break;

        case MEMORY_CHEAT_NEXT_SCAN_SAME_AS_FIRST:
            if (value==first_value) return 1;
            else return 0;
        break;

        default:
            return 0;
        break;
    }

}

int menu_memory_cheat_scan_total_results=0;
int memory_cheat_first_scan_start_low_range=23296;

void menu_memory_cheat_first_scan_start(MENU_ITEM_PARAMETERS)
{
    int total_elementos=get_efectivo_tamanyo_find_buffer();

    //Primero ponerlas todas a 0
    int i;
    for (i=0;i<total_elementos;i++) {
        menu_memory_cheat_array_list[i].matches=0;
    }

    menu_memory_cheat_scan_total_results=0;

    //Y empezar desde margen inferior indicado
    for (i=memory_cheat_first_scan_start_low_range;i<total_elementos;i++) {
        z80_byte value=peek_byte_z80_moto(i);
        int matches=memory_cheat_first_scan_matches(value);

        menu_memory_cheat_array_list[i].value_first_scan=value;
        menu_memory_cheat_array_list[i].value_last_scan=value;

        if (matches) {
            //printf ("Address %X matches condition (value=%X)\n",i,value);
            menu_memory_cheat_scan_total_results++;
        }
        menu_memory_cheat_array_list[i].matches=matches;
    }

    menu_memory_cheat_realizado_first_scan=1;



    //Si hay al menos 1, volvemos al menu anterior
    if (menu_memory_cheat_scan_total_results) {
        menu_generic_message_format("End scan","Total results: %d",menu_memory_cheat_scan_total_results);
    }
    else {
        menu_generic_message_warn("End scan","No results found");
    }

    //Impedir que desde menu_generic_message se pulse en otra ventana u F5 porque entonces se enviara una orden
    //de cierre al menu tabulado con OK, y no se quedaria la ventana de memory cheat en background
    salir_todos_menus=0;


}




void menu_memory_cheat_next_scan_start(MENU_ITEM_PARAMETERS)
{
    int total_elementos=get_efectivo_tamanyo_find_buffer();

    menu_memory_cheat_scan_total_results=0;

    int i;
    for (i=0;i<total_elementos;i++) {
        if (menu_memory_cheat_array_list[i].matches) {
            z80_byte value=peek_byte_z80_moto(i);

            int matches=memory_cheat_next_scan_matches(value,menu_memory_cheat_array_list[i].value_last_scan,menu_memory_cheat_array_list[i].value_first_scan);
            menu_memory_cheat_array_list[i].value_last_scan=value;

            if (matches) {
                //printf ("Address %X matches condition (value=%X)\n",i,value);
                menu_memory_cheat_scan_total_results++;
            }
            menu_memory_cheat_array_list[i].matches=matches;
        }
    }




    //Si hay al menos 1, volvemos al menu anterior
    if (menu_memory_cheat_scan_total_results) {
        menu_generic_message_format("End scan","Total results: %d",menu_memory_cheat_scan_total_results);
    }

    else {
        menu_generic_message_warn("End scan","No results found");
    }

    //Impedir que desde menu_generic_message se pulse en otra ventana u F5 porque entonces se enviara una orden
    //de cierre al menu tabulado con OK, y no se quedaria la ventana de memory cheat en background
    salir_todos_menus=0;

}


void menu_memory_cheat_next_scan_condition(MENU_ITEM_PARAMETERS)
{


    menu_item *array_menu_common;
    menu_item item_seleccionado;
    int retorno_menu;

    int opcion_seleccionada=memory_cheat_next_scan_condition;



    do {

        menu_add_item_menu_inicial(&array_menu_common,"",MENU_OPCION_UNASSIGNED,NULL,NULL);

        int i;
        for (i=MEMORY_CHEAT_NEXT_SCAN_EXACT_VALUE;i<=MEMORY_CHEAT_NEXT_SCAN_SAME_AS_FIRST;i++) {
            menu_add_item_menu_format(array_menu_common,MENU_OPCION_NORMAL,NULL,NULL,"%s",memory_cheat_next_scan_possible_conditions_strings[i]);
        }

        menu_add_item_menu_separator(array_menu_common);

        menu_add_ESC_item(array_menu_common);

        retorno_menu=menu_dibuja_menu_dialogo_no_title_lang(&opcion_seleccionada,&item_seleccionado,array_menu_common,"Condition");



        if ((item_seleccionado.tipo_opcion&MENU_OPCION_ESC)==0 && retorno_menu>=0) {
            //Asignar tal cual la opcion y salir
            if (opcion_seleccionada<=MEMORY_CHEAT_NEXT_SCAN_SAME_AS_FIRST) {
                memory_cheat_next_scan_condition=opcion_seleccionada;
                return;
            }

        }

    } while ( (item_seleccionado.tipo_opcion&MENU_OPCION_ESC)==0 && retorno_menu!=MENU_RETORNO_ESC && !salir_todos_menus);

    //no queremos que al pulsar ESC se cierre la ventana de memory cheat
    salir_todos_menus=0;
}

void menu_memory_cheat_next_scan_change_first_parameter(MENU_ITEM_PARAMETERS)
{
    char string_valor[4];
    sprintf (string_valor,"%02XH",memory_cheat_next_scan_condition_first_parameter);
    menu_ventana_scanf("Value: ",string_valor,4);
    memory_cheat_next_scan_condition_first_parameter=parse_string_to_number(string_valor);
}

void menu_memory_cheat_next_scan_change_second_parameter(MENU_ITEM_PARAMETERS)
{
    char string_valor[4];
    sprintf (string_valor,"%02XH",memory_cheat_next_scan_condition_second_parameter);
    menu_ventana_scanf("Value: ",string_valor,4);
    memory_cheat_next_scan_condition_second_parameter=parse_string_to_number(string_valor);
}



void menu_memory_cheat_first_scan_change_first_parameter(MENU_ITEM_PARAMETERS)
{
    char string_valor[4];
    sprintf (string_valor,"%02XH",memory_cheat_first_scan_condition_first_parameter);
    menu_ventana_scanf("Value: ",string_valor,4);
    memory_cheat_first_scan_condition_first_parameter=parse_string_to_number(string_valor);
}

void menu_memory_cheat_first_scan_change_second_parameter(MENU_ITEM_PARAMETERS)
{
    char string_valor[4];
    sprintf (string_valor,"%02XH",memory_cheat_first_scan_condition_second_parameter);
    menu_ventana_scanf("Value: ",string_valor,4);
    memory_cheat_first_scan_condition_second_parameter=parse_string_to_number(string_valor);
}





zxvision_window *menu_memory_cheat_window;


void menu_memory_cheat_overlay(void)
{

    menu_speech_set_tecla_pulsada(); //Si no, envia continuamente todo ese texto a speech

    //si ventana minimizada, no ejecutar todo el codigo de overlay
    if (menu_memory_cheat_window->is_minimized) return;


    //Print....
    //Tambien contar si se escribe siempre o se tiene en cuenta contador_segundo...

    //mostrar watches
    int i;
    int x=1;

    int digitos=4;
    if (CPU_IS_MOTOROLA) digitos=6;

    int incremento=10; //digitos+2;

    for (i=0;i<MEMORY_CHEAT_MAX_WATCHES;i++) {
        if (memory_cheat_watch_list[i].activo) {
            int direccion=memory_cheat_watch_list[i].direccion;
            zxvision_print_string_defaults_format(menu_memory_cheat_window,x,MEMORY_CHEAT_WATCHES_LINE+2,
                "%0*XH",digitos,direccion);
            zxvision_print_string_defaults_format(menu_memory_cheat_window,x,MEMORY_CHEAT_WATCHES_LINE+3,
                "%02XH (%3d)",peek_byte_z80_moto(direccion),peek_byte_z80_moto(direccion));
        }

        x +=incremento;
    }


    //Mostrar contenido
    zxvision_draw_window_contents(menu_memory_cheat_window);

}




//Almacenar la estructura de ventana aqui para que se pueda referenciar desde otros sitios
zxvision_window zxvision_window_memory_cheat;

int menu_memory_cheat_view_results_cond(void)
{
    if (menu_memory_cheat_scan_total_results) return 1;
    else return 0;
}

void menu_memory_cheat_change_watch(MENU_ITEM_PARAMETERS)
{
    int watch_id=valor_opcion;
    int direccion=-1;

    if (memory_cheat_watch_list[watch_id].activo) {
        direccion=memory_cheat_watch_list[watch_id].direccion;
        //Pedir direccion cuando es un set watch
        //Si es un write en cambio no, porque ya sabe la direccion, que es la del watch activo
        menu_memory_cheat_set_watch_or_write(direccion,watch_id,1);
    }

    //Si no tiene watch, solo podemos hacer set watch
    else {
        //Pedir direccion de set watch
        menu_memory_cheat_set_watch_or_write_no_ask_choice(2,direccion,watch_id,1);
    }
}


void menu_memory_cheat_first_scan_start_low_range(MENU_ITEM_PARAMETERS)
{
    char string_valor[8];
    sprintf (string_valor,"%XH",memory_cheat_first_scan_start_low_range);
    menu_ventana_scanf("Start Address: ",string_valor,8);
    memory_cheat_first_scan_start_low_range=parse_string_to_number(string_valor);
}




void menu_memory_cheat(MENU_ITEM_PARAMETERS)
{
	menu_espera_no_tecla();

    if (!menu_multitarea) {
        menu_warn_message("This window needs multitask enabled");
        return;
    }

    //Inicializamos array si no esta inicializado
    menu_memory_cheat_init_array();

    zxvision_window *ventana;
    ventana=&zxvision_window_memory_cheat;

	//IMPORTANTE! no crear ventana si ya existe. Esto hay que hacerlo en todas las ventanas que permiten background.
	//si no se hiciera, se crearia la misma ventana, y en la lista de ventanas activas , al redibujarse,
	//la primera ventana repetida apuntaria a la segunda, que es el mismo puntero, y redibujaria la misma, y se quedaria en bucle colgado
	//zxvision_delete_window_if_exists(ventana);

    //Crear ventana si no existe
    if (!zxvision_if_window_already_exists(ventana)) {
        int xventana,yventana,ancho_ventana,alto_ventana,is_minimized,is_maximized,ancho_antes_minimize,alto_antes_minimize;

        if (!util_find_window_geometry("memorycheat",&xventana,&yventana,&ancho_ventana,&alto_ventana,&is_minimized,&is_maximized,&ancho_antes_minimize,&alto_antes_minimize)) {
            ancho_ventana=43;
            alto_ventana=14;

            xventana=menu_center_x()-ancho_ventana/2;
            yventana=menu_center_y()-alto_ventana/2;
        }


        zxvision_new_window_gn_cim(ventana,xventana,yventana,ancho_ventana,alto_ventana,ancho_ventana-1,alto_ventana-2,"Memory Cheat",
            "memorycheat",is_minimized,is_maximized,ancho_antes_minimize,alto_antes_minimize);

        ventana->can_be_backgrounded=1;

    }

    //Si ya existe, activar esta ventana
    else {
        zxvision_activate_this_window(ventana);
    }

	zxvision_draw_window(ventana);




    menu_memory_cheat_window=ventana; //Decimos que el overlay lo hace sobre la ventana que tenemos aqui


    //cambio overlay
    zxvision_set_window_overlay(ventana,menu_memory_cheat_overlay);


    //Toda ventana que este listada en zxvision_known_window_names_array debe permitir poder salir desde aqui
    //Se sale despues de haber inicializado overlay y de cualquier otra variable que necesite el overlay
    if (zxvision_currently_restoring_windows_on_start) {
            //printf ("Saliendo de ventana ya que la estamos restaurando en startup\n");
            return;
    }


    //Forzar visibles hotkeys en esa ventana
    ventana->writing_inverse_color=1;


	menu_item *array_menu_common;
	menu_item item_seleccionado;
	int retorno_menu;
	do {

        //borrar linea de menu por si hay restos de insertar
        //zxvision_print_string_defaults_fillspc(ventana,1,6,"");
        //zxvision_print_string_defaults_fillspc(ventana,1,7,"");
        zxvision_cls(ventana);

        //Para mostrar los campos seleccionables siempre con el mismo ancho y rellenos con espacios
        char buffer_campo_limite[100];


        int max_campo_seleccionable=14;

        zxvision_print_string_defaults_fillspc(ventana,1,MEMORY_CHEAT_WATCHES_LINE,"Watches");


        //zxvision_print_string_defaults_fillspc(ventana,1,0,"First Scan");
        menu_add_item_menu_inicial_format(&array_menu_common,MENU_OPCION_NORMAL,menu_memory_cheat_first_scan_start,NULL,"[~^Start First Scan]");
        menu_add_item_menu_tabulado(array_menu_common,1,0);
        menu_add_item_menu_shortcut(array_menu_common,'s');

        int digitos=4;

        if (CPU_IS_MOTOROLA) digitos=6;

        zxvision_print_string_defaults_fillspc(ventana,20,0,"Start ~^addr:");
        sprintf(buffer_campo_limite,"%0*XH",digitos,memory_cheat_first_scan_start_low_range);
        util_add_string_spaces(buffer_campo_limite,digitos+2);
        menu_add_item_menu_format(array_menu_common,MENU_OPCION_NORMAL,menu_memory_cheat_first_scan_start_low_range,NULL,
            buffer_campo_limite);
        menu_add_item_menu_tabulado(array_menu_common,32,0);
        menu_add_item_menu_shortcut(array_menu_common,'a');
        menu_add_item_menu_campo_seleccionable(array_menu_common);


        zxvision_print_string_defaults_fillspc(ventana,1,1,"Condition:");
        sprintf(buffer_campo_limite,"%s",memory_cheat_first_scan_possible_conditions_strings[memory_cheat_first_scan_condition]);
        util_add_string_spaces(buffer_campo_limite,max_campo_seleccionable);
        menu_add_item_menu_format(array_menu_common,MENU_OPCION_NORMAL,menu_memory_cheat_first_scan_condition,NULL,
            buffer_campo_limite);
        menu_add_item_menu_tabulado(array_menu_common,12,1);
        menu_add_item_menu_campo_seleccionable(array_menu_common);

        //Mostrar parametro excepto en un caso
        if (memory_cheat_first_scan_condition!=MEMORY_CHEAT_FIRST_SCAN_UNKNOWN_VALUE) {
            menu_add_item_menu_format(array_menu_common,MENU_OPCION_NORMAL,menu_memory_cheat_first_scan_change_first_parameter,NULL,
                "%02XH",memory_cheat_first_scan_condition_first_parameter);

            menu_add_item_menu_tabulado(array_menu_common,26,1);
            menu_add_item_menu_campo_seleccionable(array_menu_common);

            if (memory_cheat_first_scan_condition==MEMORY_CHEAT_FIRST_SCAN_VALUE_BETWEEN) {
                zxvision_print_string_defaults(ventana,26+4,1,"and");
                //menu_add_item_menu_format(array_menu_common,MENU_OPCION_SEPARADOR,NULL,NULL,"and");
                menu_add_item_menu_format(array_menu_common,MENU_OPCION_NORMAL,menu_memory_cheat_first_scan_change_second_parameter,NULL,
                    "%02XH",memory_cheat_first_scan_condition_second_parameter);
                menu_add_item_menu_tabulado(array_menu_common,26+4+4,1);
                menu_add_item_menu_campo_seleccionable(array_menu_common);
            }


        }


        if (menu_memory_cheat_realizado_first_scan) {

            menu_add_item_menu_format(array_menu_common,MENU_OPCION_NORMAL,menu_memory_cheat_view_results,menu_memory_cheat_view_results_cond,
                "[~^View Results (%d)]",menu_memory_cheat_scan_total_results);
            menu_add_item_menu_tabulado(array_menu_common,1,3);
            menu_add_item_menu_shortcut(array_menu_common,'v');

            menu_add_item_menu_format(array_menu_common,MENU_OPCION_NORMAL,menu_memory_cheat_next_scan_start,NULL,"[Run ~^Next Scan]");
            menu_add_item_menu_tabulado(array_menu_common,1,5);
            menu_add_item_menu_shortcut(array_menu_common,'n');

            zxvision_print_string_defaults_fillspc(ventana,1,6,"Condition:");


            max_campo_seleccionable=19;
            sprintf(buffer_campo_limite,"%s",memory_cheat_next_scan_possible_conditions_strings[memory_cheat_next_scan_condition]);
            util_add_string_spaces(buffer_campo_limite,max_campo_seleccionable);
            menu_add_item_menu_format(array_menu_common,MENU_OPCION_NORMAL,menu_memory_cheat_next_scan_condition,NULL,
                buffer_campo_limite);
            //menu_add_item_menu_format(array_menu_common,MENU_OPCION_NORMAL,menu_memory_cheat_next_scan_condition,NULL,
            //    "%s",memory_cheat_next_scan_possible_conditions_strings[memory_cheat_next_scan_condition]);
            menu_add_item_menu_tabulado(array_menu_common,12,6);
            menu_add_item_menu_campo_seleccionable(array_menu_common);


            //Mostrar parametro excepto
            if (
                memory_cheat_next_scan_condition!=MEMORY_CHEAT_NEXT_SCAN_INCREASED_VALUE &&
                memory_cheat_next_scan_condition!=MEMORY_CHEAT_NEXT_SCAN_DECREASED_VALUE &&
                memory_cheat_next_scan_condition!=MEMORY_CHEAT_NEXT_SCAN_CHANGED_VALUE &&
                memory_cheat_next_scan_condition!=MEMORY_CHEAT_NEXT_SCAN_UNCHANGED_VALUE &&
                memory_cheat_next_scan_condition!=MEMORY_CHEAT_NEXT_SCAN_SAME_AS_FIRST
            ) {
                menu_add_item_menu_format(array_menu_common,MENU_OPCION_NORMAL,menu_memory_cheat_next_scan_change_first_parameter,NULL,
                    "%02XH",memory_cheat_next_scan_condition_first_parameter);
                menu_add_item_menu_tabulado(array_menu_common,31,6);
                menu_add_item_menu_campo_seleccionable(array_menu_common);

                if (memory_cheat_next_scan_condition==MEMORY_CHEAT_NEXT_SCAN_VALUE_BETWEEN) {
                    zxvision_print_string_defaults(ventana,31+4,6,"and");
                    //menu_add_item_menu_format(array_menu_common,MENU_OPCION_SEPARADOR,NULL,NULL,"and");
                    menu_add_item_menu_format(array_menu_common,MENU_OPCION_NORMAL,menu_memory_cheat_next_scan_change_second_parameter,NULL,
                        "%02XH",memory_cheat_next_scan_condition_second_parameter);
                    menu_add_item_menu_tabulado(array_menu_common,31+4+4,6);
                    menu_add_item_menu_campo_seleccionable(array_menu_common);
                }
            }

        }

        int i;
        int x=1;
        for (i=0;i<MEMORY_CHEAT_MAX_WATCHES;i++) {

            menu_add_item_menu_format(array_menu_common,MENU_OPCION_NORMAL,menu_memory_cheat_change_watch,NULL,
                "[Watch %d]",i+1);
            menu_add_item_menu_tabulado(array_menu_common,x,MEMORY_CHEAT_WATCHES_LINE+1);
            menu_add_item_menu_valor_opcion(array_menu_common,i);

            x+=10;
        }



		//Nombre de ventana solo aparece en el caso de stdout
		retorno_menu=menu_dibuja_menu_no_title_lang(&audio_visual_realtape_opcion_seleccionada,&item_seleccionado,array_menu_common,"Memory Cheat" );

		if (retorno_menu!=MENU_RETORNO_BACKGROUND) {

			//En caso de menus tabulados, es responsabilidad de este de borrar la ventana

			if ((item_seleccionado.tipo_opcion&MENU_OPCION_ESC)==0 && retorno_menu>=0) {
				//llamamos por valor de funcion
				if (item_seleccionado.menu_funcion!=NULL) {
					//printf ("actuamos por funcion\n");
					item_seleccionado.menu_funcion(item_seleccionado.valor_opcion);

				}
			}
		}

	} while ( (item_seleccionado.tipo_opcion&MENU_OPCION_ESC)==0 && retorno_menu!=MENU_RETORNO_ESC && !salir_todos_menus && retorno_menu!=MENU_RETORNO_BACKGROUND);


    //En caso de menus tabulados, suele ser necesario esto. Si no, la ventana se quedaria visible

//if (retorno_menu==MENU_RETORNO_ESC) printf("Retorno con ESC\n");
//if (retorno_menu==MENU_RETORNO_BACKGROUND) printf("Retorno con background\n");
//if (retorno_menu==MENU_RETORNO_NORMAL) printf("Retorno normal\n");
//printf("retorno_menu: %d\n",retorno_menu);

    //Grabar geometria ventana
    util_add_window_geometry_compact(ventana);


    if (retorno_menu==MENU_RETORNO_BACKGROUND) {
        zxvision_message_put_window_background();
    }

    else {
        //En caso de menus tabulados, es responsabilidad de este de liberar ventana
        zxvision_destroy_window(ventana);
    }






}






void menu_find(MENU_ITEM_PARAMETERS)
{
        menu_item *array_menu_find;
        menu_item item_seleccionado;
        int retorno_menu;




        do {

                menu_add_item_menu_inicial_format(&array_menu_find,MENU_OPCION_NORMAL,menu_find_bytes,NULL,"Find bytes");
                menu_add_item_menu_tooltip(array_menu_find,"Find several bytes on memory");
                menu_add_item_menu_ayuda(array_menu_find,"Find several bytes on the 64 KB of mapped memory, considering the last address found (if any)");

								menu_add_item_menu_format(array_menu_find,MENU_OPCION_NORMAL,menu_find_lives,NULL,"Find lives address");
                menu_add_item_menu_tooltip(array_menu_find,"Find memory pointer where lives are located");
                menu_add_item_menu_ayuda(array_menu_find,"Find memory pointer where lives are located)");

                menu_add_item_menu_format(array_menu_find,MENU_OPCION_NORMAL,menu_memory_cheat,NULL,"Memory Cheat");
                menu_add_item_menu_tooltip(array_menu_find,"Find bytes in memory with some conditions");
                menu_add_item_menu_ayuda(array_menu_find,"Find bytes in memory with some conditions");
                menu_add_item_menu_genera_ventana(array_menu_find);


                menu_add_item_menu(array_menu_find,"",MENU_OPCION_SEPARADOR,NULL,NULL);


                menu_add_ESC_item(array_menu_find);

                retorno_menu=menu_dibuja_menu(&find_opcion_seleccionada,&item_seleccionado,array_menu_find,
                    "Find menu","Menú Find","Menú Find" );



                if ((item_seleccionado.tipo_opcion&MENU_OPCION_ESC)==0 && retorno_menu>=0) {
                        //llamamos por valor de funcion
                        if (item_seleccionado.menu_funcion!=NULL) {
                                //printf ("actuamos por funcion\n");
                                item_seleccionado.menu_funcion(item_seleccionado.valor_opcion);

                        }
                }

        } while ( (item_seleccionado.tipo_opcion&MENU_OPCION_ESC)==0 && retorno_menu!=MENU_RETORNO_ESC && !salir_todos_menus);
}


void old_menu_debug_view_basic_variables(MENU_ITEM_PARAMETERS)
{

    menu_first_aid("debug_variables");

    char *results_buffer=util_malloc_max_texto_generic_message("Can not allocate memory for showing variables");

	debug_view_basic_variables(results_buffer,MAX_TEXTO_GENERIC_MESSAGE);

    menu_generic_message_format("Basic Variables","%s",results_buffer);

    free(results_buffer);
}


zxvision_window *menu_view_basic_variables_window;



int menu_view_basic_variables_recargar=0;

#define MENU_VIEW_BASIC_VARIABLES_MAX_LINE_LENGTH 100

z80_long_int menu_view_basic_variables_last_crc32=0;

//Calcular crc32 de las variables por saber si se modifica para volver a renderizar
z80_long_int menu_view_basic_variables_get_crc32(void)
{


    int start_address=debug_view_basic_variables_get_start();

    //A veces la longitud es menos que esto que obtenemos, pero con esto ya nos sirve
    int length=debug_get_eline_value()-1-start_address;
    //debug_view_basic_variables_get_length_variables();

    //printf("Start %5d length: %d (suma=%d) (e_line)=%d\n",start_address,length,start_address+length,debug_get_eline_value());

    if (length<1) {
        //escribir_socket(misocket,"ERROR. Length must be >0");
        return 0;
    }


    //Copiar contenido memoria segun memory zone activa a buffer de memoria temporal
    z80_byte *memoria_temporal;
    memoria_temporal=malloc(length);
    if (memoria_temporal==NULL) cpu_panic("Can not allocate memory for crc32 calculation");

    int longitud_copiar=length;
    int i;

    for (i=0;longitud_copiar>0;i++,longitud_copiar--) {
        z80_byte byte_leido=peek_byte_no_time(start_address+i);
        memoria_temporal[i]=byte_leido;
    }



    z80_long_int crc32=util_crc32_calculation(0,memoria_temporal,length);


    free(memoria_temporal);

    return crc32;

}



void menu_view_basic_variables_overlay(void)
{

    menu_speech_set_tecla_pulsada(); //Si no, envia continuamente todo ese texto a speech

    //si ventana minimizada, no ejecutar todo el codigo de overlay
    if (menu_view_basic_variables_window->is_minimized) return;

    if (!MACHINE_IS_SPECTRUM && !MACHINE_IS_ZX8081) {
        zxvision_cls(menu_view_basic_variables_window);
        zxvision_print_string_defaults_fillspc_format(menu_view_basic_variables_window,1,0,"Only available on Spectrum and ZX80/81");
    }

    else {


        //Print....
        //Tambien contar si se escribe siempre o se tiene en cuenta contador_segundo...

        z80_long_int crc32=menu_view_basic_variables_get_crc32();
        //printf("Obtenido crc: %X\n",crc32);
        if (crc32!=menu_view_basic_variables_last_crc32) {
            //printf("CRC modificado\n");
            menu_view_basic_variables_last_crc32=crc32;
            menu_view_basic_variables_recargar=1;
        }

        if (!menu_view_basic_variables_recargar) {
            //Ver si longitud variables es 0
            int start_address=debug_view_basic_variables_get_start();

            //A veces la longitud es menos que esto que obtenemos, pero con esto ya nos sirve
            int length=debug_get_eline_value()-1-start_address;

            if (!length) {
                zxvision_cls(menu_view_basic_variables_window);
                zxvision_print_string_defaults_fillspc_format(menu_view_basic_variables_window,1,0,"No variables");
            }
        }

        if (menu_view_basic_variables_recargar) {
            char *results_buffer=util_malloc_max_texto_generic_message("Can not allocate memory for showing basic variables");
            debug_view_basic_variables(results_buffer,MAX_TEXTO_GENERIC_MESSAGE);

            int lineas=util_count_lines(results_buffer);

            //Mostrar una a una todas las lineas

            //Ajustar alto ventana. Al menos una linea aunque solo sea para mostrar error
            zxvision_set_total_height(menu_view_basic_variables_window,lineas+1);

            char buffer_linea[MENU_VIEW_BASIC_VARIABLES_MAX_LINE_LENGTH+1];
            char *puntero_leer=results_buffer;
            int i;
            for (i=0;i<menu_view_basic_variables_window->total_height;i++) {
                int columna=0;
                while (*puntero_leer && (*puntero_leer)!='\n') {
                    if (columna<MENU_VIEW_BASIC_VARIABLES_MAX_LINE_LENGTH) {
                        buffer_linea[columna++]=*puntero_leer;
                    }
                    puntero_leer++;
                }
                buffer_linea[columna++]=0;
                if (*puntero_leer=='\n') puntero_leer++;

                zxvision_print_string_defaults_fillspc_format(menu_view_basic_variables_window,1,i,"%s",buffer_linea);
            }


            free(results_buffer);

            menu_view_basic_variables_recargar=0;

        }
    }

    //Mostrar contenido
    zxvision_draw_window_contents(menu_view_basic_variables_window);

}






//Almacenar la estructura de ventana aqui para que se pueda referenciar desde otros sitios
zxvision_window zxvision_window_view_basic_variables;


//Nota: menu_view_basic_variables y menu_view_gosub_stack son casi la misma ventana, se comportan igual,
//solo difieren en qué información muestran. Quizá en un futuro se podría hacer una ventana genérica que pudiese
//mostrar un contenido de texto en background y que refrescase bajo ciertas condiciones
void menu_view_basic_variables(MENU_ITEM_PARAMETERS)
{
	menu_espera_no_tecla();

    if (!menu_multitarea) {
        menu_warn_message("This window needs multitask enabled");
        return;
    }

    menu_first_aid("debug_variables");

    zxvision_window *ventana;
    ventana=&zxvision_window_view_basic_variables;

	//IMPORTANTE! no crear ventana si ya existe. Esto hay que hacerlo en todas las ventanas que permiten background.
	//si no se hiciera, se crearia la misma ventana, y en la lista de ventanas activas , al redibujarse,
	//la primera ventana repetida apuntaria a la segunda, que es el mismo puntero, y redibujaria la misma, y se quedaria en bucle colgado
	//zxvision_delete_window_if_exists(ventana);

    //Crear ventana si no existe
    if (!zxvision_if_window_already_exists(ventana)) {
        int xventana,yventana,ancho_ventana,alto_ventana,is_minimized,is_maximized,ancho_antes_minimize,alto_antes_minimize;

        if (!util_find_window_geometry("viewbasicvariables",&xventana,&yventana,&ancho_ventana,&alto_ventana,&is_minimized,&is_maximized,&ancho_antes_minimize,&alto_antes_minimize)) {
            ancho_ventana=30;
            alto_ventana=20;

            xventana=menu_center_x()-ancho_ventana/2;
            yventana=menu_center_y()-alto_ventana/2;
        }


        zxvision_new_window_gn_cim(ventana,xventana,yventana,ancho_ventana,alto_ventana,ancho_ventana-1,alto_ventana-2,"Basic Variables",
            "viewbasicvariables",is_minimized,is_maximized,ancho_antes_minimize,alto_antes_minimize);

        ventana->can_be_backgrounded=1;

    }

    //Si ya existe, activar esta ventana
    else {
        zxvision_activate_this_window(ventana);
    }

	zxvision_draw_window(ventana);

	z80_byte tecla;


	int salir=0;

    menu_view_basic_variables_recargar=1;


    menu_view_basic_variables_window=ventana; //Decimos que el overlay lo hace sobre la ventana que tenemos aqui


    //cambio overlay
    zxvision_set_window_overlay(ventana,menu_view_basic_variables_overlay);


    //Toda ventana que este listada en zxvision_known_window_names_array debe permitir poder salir desde aqui
    //Se sale despues de haber inicializado overlay y de cualquier otra variable que necesite el overlay
    if (zxvision_currently_restoring_windows_on_start) {
        //printf ("Saliendo de ventana ya que la estamos restaurando en startup\n");
        return;
    }

    do {


		tecla=zxvision_common_getkey_refresh();
        zxvision_handle_cursors_pgupdn(ventana,tecla);

        switch (tecla) {

            //Salir con ESC
            case 2:
                salir=1;
            break;

            //O tecla background
            case 3:
                salir=1;
            break;
        }


    } while (salir==0);


	util_add_window_geometry_compact(ventana);

	if (tecla==3) {
		zxvision_message_put_window_background();
	}

	else {
		zxvision_destroy_window(ventana);
	}


}



/*
Fin de Template de ventana de menu que se puede enviar a background
*/



//Indica a la funcion de overlay cual es la ventana
zxvision_window *menu_debug_view_sensors_overlay_window;

//Contador de segundo para hacer que el overlay solo se redibuje un numero de veces por segundo y no siempre
int menu_debug_view_sensors_contador_segundo_anterior;



//TODO esto para hacer una prueba rapida. finalmente esto se hara guardando el short_name de cada sensor
int temporal_current_view_sensors_actual=0;

//Usado para el meter de tipo volumen
//int menu_debug_view_sensors_valor_anterior=0;

int menu_debug_view_sensors_tipo=0;

#define MENU_VIEW_SENSORS_START_X 1
#define MENU_VIEW_SENSORS_START_Y 3

//array de sensores en pantalla

#define MENU_SENSORS_SEPARACION_ENTRE_COLUMNAS 16
#define MENU_SENSORS_SEPARACION_ENTRE_FILAS 10

menu_debug_view_sensors_list menu_debug_view_sensors_list_sensors[MENU_VIEW_SENSORS_TOTAL_ELEMENTS]={
    {"",MENU_SENSORS_SEPARACION_ENTRE_FILAS*0,MENU_SENSORS_SEPARACION_ENTRE_COLUMNAS*0,0,0},
    {"",MENU_SENSORS_SEPARACION_ENTRE_FILAS*0,MENU_SENSORS_SEPARACION_ENTRE_COLUMNAS*1,0,0},
    {"",MENU_SENSORS_SEPARACION_ENTRE_FILAS*0,MENU_SENSORS_SEPARACION_ENTRE_COLUMNAS*2,0,0},
    {"",MENU_SENSORS_SEPARACION_ENTRE_FILAS*0,MENU_SENSORS_SEPARACION_ENTRE_COLUMNAS*3,0,0},
    {"",MENU_SENSORS_SEPARACION_ENTRE_FILAS*0,MENU_SENSORS_SEPARACION_ENTRE_COLUMNAS*4,0,0},

    {"",MENU_SENSORS_SEPARACION_ENTRE_FILAS*1,MENU_SENSORS_SEPARACION_ENTRE_COLUMNAS*0,0,0},
    {"",MENU_SENSORS_SEPARACION_ENTRE_FILAS*1,MENU_SENSORS_SEPARACION_ENTRE_COLUMNAS*1,0,0},
    {"",MENU_SENSORS_SEPARACION_ENTRE_FILAS*1,MENU_SENSORS_SEPARACION_ENTRE_COLUMNAS*2,0,0},
    {"",MENU_SENSORS_SEPARACION_ENTRE_FILAS*1,MENU_SENSORS_SEPARACION_ENTRE_COLUMNAS*3,0,0},
    {"",MENU_SENSORS_SEPARACION_ENTRE_FILAS*1,MENU_SENSORS_SEPARACION_ENTRE_COLUMNAS*4,0,0},

    {"",MENU_SENSORS_SEPARACION_ENTRE_FILAS*2,MENU_SENSORS_SEPARACION_ENTRE_COLUMNAS*0,0,0},
    {"",MENU_SENSORS_SEPARACION_ENTRE_FILAS*2,MENU_SENSORS_SEPARACION_ENTRE_COLUMNAS*1,0,0},
    {"",MENU_SENSORS_SEPARACION_ENTRE_FILAS*2,MENU_SENSORS_SEPARACION_ENTRE_COLUMNAS*2,0,0},
    {"",MENU_SENSORS_SEPARACION_ENTRE_FILAS*2,MENU_SENSORS_SEPARACION_ENTRE_COLUMNAS*3,0,0},
    {"",MENU_SENSORS_SEPARACION_ENTRE_FILAS*2,MENU_SENSORS_SEPARACION_ENTRE_COLUMNAS*4,0,0},

};

int menu_view_sensors_cursor_fila=0;
int menu_view_sensors_cursor_columna=0;
int menu_view_sensors_cursor_visible=0;

void menu_debug_view_sensors_print_cursor(zxvision_window *ventana)
{

    int fila=menu_view_sensors_cursor_fila;
    int columna=menu_view_sensors_cursor_columna;

    /*
    puntos del cuadrado:

      x1,y1      x2,y1
       -----------
       |         |
       |         |
       -----------
      x1,y2      x2,y2
    */

    int x1=(MENU_VIEW_SENSORS_START_X+columna* MENU_SENSORS_SEPARACION_ENTRE_COLUMNAS)*menu_char_width-1; //-1 para que no roce por la izquierda con el texto
    int y1=(MENU_VIEW_SENSORS_START_Y-1+fila * MENU_SENSORS_SEPARACION_ENTRE_FILAS)   *menu_char_height;

    int x2=(MENU_VIEW_SENSORS_START_X+(columna+1)* MENU_SENSORS_SEPARACION_ENTRE_COLUMNAS)*menu_char_width-1; //-1 para que no toque el pixel siguiente
    int y2=(MENU_VIEW_SENSORS_START_Y-2+(fila+1) * MENU_SENSORS_SEPARACION_ENTRE_FILAS)   *menu_char_height-1; //-1 para que no toque el pixel siguiente

    //arriba
    zxvision_draw_line(ventana,x1,y1,x2,y1,ESTILO_GUI_TINTA_NORMAL,zxvision_putpixel);
    //abajo
    zxvision_draw_line(ventana,x1,y2,x2,y2,ESTILO_GUI_TINTA_NORMAL,zxvision_putpixel);
    //izquierda
    zxvision_draw_line(ventana,x1,y1,x1,y2,ESTILO_GUI_TINTA_NORMAL,zxvision_putpixel);
    //derecha
    zxvision_draw_line(ventana,x2,y1,x2,y2,ESTILO_GUI_TINTA_NORMAL,zxvision_putpixel);



/*
    //Rellenar cuadrado de color del cursor
    //Nota: no puedo rellenar todo el texto simplemente con lineas porque esto ocultaria el texto del sensor,
    //pues al hacer render de pantalla siempre primero se escribe el texto y luego la rutina de overlay (o sea esta)
    //Parte de arriba hasta texto. 8 pixeles de alto
    franja_color_y_inicio=y1+1;
    for (i=franja_color_y_inicio;i<=franja_color_y_inicio+6;i++) {
        zxvision_draw_line(ventana,x1+1,i,x2-1,i,ESTILO_GUI_PAPEL_SELECCIONADO,zxvision_putpixel);
    }

    //Parte desde abajo del texto incluyendo sensor
    franja_color_y_inicio=y1+16;
    for (i=franja_color_y_inicio;i<=y2-1;i++) {
        zxvision_draw_line(ventana,x1+1,i,x2-1,i,ESTILO_GUI_PAPEL_SELECCIONADO,zxvision_putpixel);
    }

    //Si sensor vacio, si que dibujamos esa franja
    int offset_array=menu_view_sensors_cursor_fila*MENU_VIEW_SENSORS_TOTAL_COLUMNS+menu_view_sensors_cursor_columna;

    if (menu_debug_view_sensors_list_sensors[offset_array].short_name[0]==0) {
        franja_color_y_inicio=y1+8;
        for (i=franja_color_y_inicio;i<=franja_color_y_inicio+7;i++) {
            zxvision_draw_line(ventana,x1+1,i,x2-1,i,ESTILO_GUI_PAPEL_SELECCIONADO,zxvision_putpixel);
        }
    }
    */

}



//La funcion de overlay
void menu_debug_view_sensors_overlay_window_overlay(void)
{

    menu_speech_set_tecla_pulsada(); //Si no, envia continuamente todo ese texto a speech

    //si ventana minimizada, no ejecutar todo el codigo de overlay
    if (menu_debug_view_sensors_overlay_window->is_minimized) return; //Sustituir xxxx_overlay_window por lo que convenga

    //printf("overlay view sensors %d\n",contador_segundo);

    zxvision_window *ventana;

    ventana=menu_debug_view_sensors_overlay_window;



    //esto hara ejecutar esto 2 veces por segundo
    if ( ((contador_segundo%500) == 0 && menu_debug_view_sensors_contador_segundo_anterior!=contador_segundo) ) {

        menu_debug_view_sensors_contador_segundo_anterior=contador_segundo;
        //printf ("Refrescando. contador_segundo=%d\n",contador_segundo);



    }

    if (menu_view_sensors_cursor_visible) menu_debug_view_sensors_print_cursor(ventana);


    int fila_texto;
    int columna_texto;


    int fila,columna;

    for (fila=0;fila<MENU_VIEW_SENSORS_TOTAL_ROWS;fila++) {
        for (columna=0;columna<MENU_VIEW_SENSORS_TOTAL_COLUMNS;columna++) {
            int offset_array=fila*MENU_VIEW_SENSORS_TOTAL_COLUMNS+columna;

            fila_texto=MENU_VIEW_SENSORS_START_Y+menu_debug_view_sensors_list_sensors[offset_array].fila;
            columna_texto=MENU_VIEW_SENSORS_START_X+menu_debug_view_sensors_list_sensors[offset_array].columna;

            char *short_name;
            //temporal esto hacerlo luego mejor
            short_name=menu_debug_view_sensors_list_sensors[offset_array].short_name;
            int tipo=menu_debug_view_sensors_list_sensors[offset_array].tipo;
            int valor_en_vez_de_perc=menu_debug_view_sensors_list_sensors[offset_array].valor_en_vez_de_perc;


            int tinta_texto=ESTILO_GUI_TINTA_NORMAL;
            int papel_texto=ESTILO_GUI_PAPEL_NORMAL;

            //Cambio color si cursor ahi
            if (fila==menu_view_sensors_cursor_fila && columna==menu_view_sensors_cursor_columna && menu_view_sensors_cursor_visible) {
                //texto en color inverso si esta el cursor ahi
                tinta_texto=ESTILO_GUI_TINTA_SELECCIONADO;
                papel_texto=ESTILO_GUI_PAPEL_SELECCIONADO;

                /*int i,j;
                for (j=0;j<MENU_SENSORS_SEPARACION_ENTRE_FILAS-1;j++) {
                    for (i=0;i<MENU_SENSORS_SEPARACION_ENTRE_COLUMNAS;i++) {
                        zxvision_print_char_simple(ventana,columna_texto+i,fila_texto+j-1,tinta_texto,papel_texto,0,'X');
                    }
                }
                */
            }

            if (short_name[0]) {

                zxvision_widgets_draw_metter_common_by_shortname
                    (ventana,columna_texto,fila_texto,short_name,tipo,valor_en_vez_de_perc,tinta_texto,papel_texto,
                    MENU_SENSORS_SEPARACION_ENTRE_COLUMNAS);
            }


        }
    }






    //Siempre hará el dibujado de contenido para evitar que cuando esta en background, otra ventana por debajo escriba algo,
    //y entonces como esta no redibuja siempre, al no escribir encima, se sobreescribe este contenido con el de otra ventana
    //En ventanas que no escriben siempre su contenido, siempre deberia estar zxvision_draw_window_contents que lo haga siempre
    zxvision_draw_window_contents(ventana);
}

//La ventana tal cual que creamos. Es la estructura, no un puntero
zxvision_window zxvision_window_view_sensors;




int menu_debug_view_sensors_get_sensor_tipo(int tipo)
{
    int opcion_seleccionada=tipo;

    menu_item *array_menu_common;
    menu_item item_seleccionado;
    int retorno_menu;


    menu_add_item_menu_inicial(&array_menu_common,"",MENU_OPCION_UNASSIGNED,NULL,NULL);

    int i;

    for (i=0;i<ZXVISION_TOTAL_WIDGET_TYPES;i++) {
        menu_add_item_menu_format(array_menu_common,MENU_OPCION_NORMAL,NULL,NULL,zxvision_widget_types_names[i]);
    }



    menu_add_item_menu_separator(array_menu_common);

    menu_add_ESC_item(array_menu_common);

    retorno_menu=menu_dibuja_menu_dialogo_no_title_lang(&opcion_seleccionada,&item_seleccionado,array_menu_common,"Meter Type" );



    if ((item_seleccionado.tipo_opcion&MENU_OPCION_ESC)==0 && retorno_menu>=0) {
        //Si se pulsa Enter
        return opcion_seleccionada;
    }

    return -1;
}

//Retorna:
//0 si selecciona vacio
//1... si selecciona un sensor
//-1 si sale con ESC
int menu_debug_view_sensors_get_sensor_item(int sensor_id)
{


    int opcion_seleccionada=sensor_id;

    menu_item *array_menu_common;
    menu_item item_seleccionado;
    int retorno_menu;


    menu_add_item_menu_inicial_format(&array_menu_common,MENU_OPCION_NORMAL,NULL,NULL,"(Empty)");

    int i;
    for (i=0;i<TOTAL_SENSORS;i++) {

        menu_add_item_menu_format(array_menu_common,MENU_OPCION_NORMAL,NULL,NULL,sensors_array[i].long_name);
        //menu_add_item_menu_tooltip(array_menu_common,"Sensors that return times, they are expressed in microseconds");
        menu_add_item_menu_ayuda(array_menu_common,
            "Sensors may be type:\n"
            "- A natural value, like FPS, Render time, or Volume of an Audio Chip Channel\n"
            "- A percentage value, like %CPU usage\n"
            "\n"
            "Sensors that return times, they are expressed in microseconds\n"
            "Some sensors will change its colour to a warning colour (usually red) when they are beyond normal values\n"
            "\n"
            "Sensors can be shown on the window by the sensor value or a calculated percentage, for example:\n"
            "If a sensor returns time in microseconds, and the sensor is 10000, the calculated percentage of a total 20000, will be 50%\n"
            "These sensors which are naturally a percentage value (like %CPU usage) showing the absolute value or a calculated percentage, "
            "will show exactly the same value (which is totally logic by the way)"
            );

    }


    menu_add_item_menu_separator(array_menu_common);

    menu_add_ESC_item(array_menu_common);

    retorno_menu=menu_dibuja_menu_no_title_lang(&opcion_seleccionada,&item_seleccionado,array_menu_common,"Sensors" );



    if ((item_seleccionado.tipo_opcion&MENU_OPCION_ESC)==0 && retorno_menu>=0) {
        //Si se pulsa Enter
        return opcion_seleccionada;
    }

    return -1;

}


void menu_view_sensors_fondo_cursor(zxvision_window *ventana,int tinta,int papel)
{

    int offset_array=menu_view_sensors_cursor_fila*MENU_VIEW_SENSORS_TOTAL_COLUMNS+menu_view_sensors_cursor_columna;

    int fila_texto_cursor,columna_texto_cursor;
    //Rellenar color fondo donde esta cursor
    fila_texto_cursor=MENU_VIEW_SENSORS_START_Y+menu_debug_view_sensors_list_sensors[offset_array].fila;
    columna_texto_cursor=MENU_VIEW_SENSORS_START_X+menu_debug_view_sensors_list_sensors[offset_array].columna;


    int i,j;
    for (j=0;j<MENU_SENSORS_SEPARACION_ENTRE_FILAS-1;j++) {
        for (i=0;i<MENU_SENSORS_SEPARACION_ENTRE_COLUMNAS;i++) {
            zxvision_print_char_simple(ventana,columna_texto_cursor+i,fila_texto_cursor+j-1,
                    tinta,papel,0,' ');
        }
    }
}

int menu_view_sensors_mouse_in_zone_widgets(int *columna,int *fila)
{

    if (!si_menu_mouse_en_ventana_no_en_scrolls()) return 0;

    int cursor_mouse_y=menu_mouse_y;
    //empieza dentro de ventana en la 1
    cursor_mouse_y--;

    int cursor_mouse_x=menu_mouse_x;

    cursor_mouse_x -=MENU_VIEW_SENSORS_START_X;
    cursor_mouse_y -=(MENU_VIEW_SENSORS_START_Y-1); //-1 porque el cursor esta 1 fila por encima

    if (cursor_mouse_x>=0 && cursor_mouse_y>=0) {

        //Ajustar a que widget apuntamos
        cursor_mouse_x /=MENU_SENSORS_SEPARACION_ENTRE_COLUMNAS;
        cursor_mouse_y /=MENU_SENSORS_SEPARACION_ENTRE_FILAS;

        //printf("widget %d %d\n",cursor_mouse_x,cursor_mouse_y);

        if (cursor_mouse_x>=0 && cursor_mouse_x<MENU_VIEW_SENSORS_TOTAL_COLUMNS &&
            cursor_mouse_y>=0 && cursor_mouse_y<MENU_VIEW_SENSORS_TOTAL_ROWS) {
                //printf("en rango\n");

                //movemos cursor
                if (columna!=NULL) *columna=cursor_mouse_x;
                if (fila!=NULL) *fila=cursor_mouse_y;

                return 1;
            }
    }

    return 0;
}

void menu_debug_view_sensors(MENU_ITEM_PARAMETERS)
{

    menu_espera_no_tecla();
	menu_reset_counters_tecla_repeticion();

    if (!menu_multitarea) {
        menu_warn_message("This window needs multitask enabled");
        return;
    }

    //Nuestro puntero apunta a la estructura que hay fuera, por comodidad de usar el nombre de puntero "ventana"
    zxvision_window *ventana;
    ventana=&zxvision_window_view_sensors;

    //IMPORTANTE! no crear ventana si ya existe. Esto hay que hacerlo en todas las ventanas que permiten background.
    //si no se hiciera, se crearia la misma ventana, y en la lista de ventanas activas , al redibujarse,
    //la primera ventana repetida apuntaria a la segunda, que es el mismo puntero, y redibujaria la misma, y se quedaria en bucle colgado
    //zxvision_delete_window_if_exists(ventana);

    //Crear ventana si no existe
    if (!zxvision_if_window_already_exists(ventana)) {

        int x_ventana,y_ventana,ancho_ventana,alto_ventana,is_minimized,is_maximized,ancho_antes_minimize,alto_antes_minimize;

        //Recuperar geometria
        if (!util_find_window_geometry("viewsensors",&x_ventana,&y_ventana,&ancho_ventana,&alto_ventana,&is_minimized,&is_maximized,&ancho_antes_minimize,&alto_antes_minimize)) {
            alto_ventana=MENU_VIEW_SENSORS_TOTAL_ROWS*MENU_SENSORS_SEPARACION_ENTRE_FILAS+MENU_VIEW_SENSORS_START_Y;
            ancho_ventana=MENU_VIEW_SENSORS_TOTAL_COLUMNS*MENU_SENSORS_SEPARACION_ENTRE_COLUMNAS+MENU_VIEW_SENSORS_START_X+1;

            x_ventana=scr_get_menu_width()-ancho_ventana; //Al ser tan ancho no cabe centrado. Hacemos que quede pegado a la derecha
            y_ventana=menu_center_y()-alto_ventana/2;


        }

        //printf("%d %d %d %d\n",x_ventana,y_ventana,ancho_ventana,alto_ventana);

        //Crear ventana
        //zxvision_new_window(ventana,x_ventana,y_ventana,ancho_ventana,alto_ventana,ancho_ventana-1,alto_ventana-2,"View Sensors");

        zxvision_new_window_gn_cim(ventana,x_ventana,y_ventana,ancho_ventana,alto_ventana,ancho_ventana-1,alto_ventana-2,"View Sensors","viewsensors",
            is_minimized,is_maximized,ancho_antes_minimize,alto_antes_minimize);

        //Se puede ir a background
        ventana->can_be_backgrounded=1;
        //indicar nombre del grabado de geometria
        //strcpy(ventana->geometry_name,"viewsensors");
        //Permitir hotkeys desde raton. Parece que incompatible con pulsar boton y simular enter
        //ventana->can_mouse_send_hotkeys=1;
        //restaurar estado minimizado de ventana
        //ventana->is_minimized=is_minimized;

        //decimos que tiene que borrar fondo cada vez al redibujar
        //por tanto es como decirle que no use cache de putchar
        //dado que el fondo de texto es casi todo texto con caracter " " eso borra los pixeles que metemos con overlay del frame anterior
        ventana->must_clear_cache_on_draw=1;

    }

    //Si ya existe, activar esta ventana
    else {

        zxvision_activate_this_window(ventana);
    }

    //Y dibujar la ventana
    zxvision_draw_window(ventana);


    menu_debug_view_sensors_overlay_window=ventana; //Decimos que el overlay lo hace sobre la ventana que tenemos aqui

    //Cambiamos funcion overlay de texto de menu

    //cambio overlay
    zxvision_set_window_overlay(ventana,menu_debug_view_sensors_overlay_window_overlay);

    //Toda ventana que este listada en zxvision_known_window_names_array debe permitir poder salir desde aqui
    //Se sale despues de haber inicializado overlay y de cualquier otra variable que necesite el overlay
    if (zxvision_currently_restoring_windows_on_start) {
        //printf ("Saliendo de ventana ya que la estamos restaurando en startup\n");
        return;
    }


    menu_view_sensors_cursor_visible=1;

    z80_byte tecla;

    //int antes_mouse_left=0;

    int antes_menu_mouse_x=menu_mouse_x;
    int antes_menu_mouse_y=menu_mouse_y;

    //Y esperar escape (2) o tecla background (3)
    do {

        //borrar rastros de textos anteriores
        zxvision_cls(ventana);

        //Forzar a mostrar atajos
		z80_bit antes_menu_writing_inverse_color;
		antes_menu_writing_inverse_color.v=menu_writing_inverse_color.v;
		menu_writing_inverse_color.v=1;

        zxvision_print_string_defaults(ventana,1,0,"Move: Cursors. ~~E~~n~~t~~e~~r: Select Sensor. ~~Type. ~~Abs/Perc");

        //Restaurar comportamiento atajos
		menu_writing_inverse_color.v=antes_menu_writing_inverse_color.v;

        char *short_name;
        int menu_debug_view_sensors_tipo;

        //menu_debug_view_sensors_print_cursor(ventana,menu_view_sensors_cursor_fila,menu_view_sensors_cursor_columna);


        int offset_array=menu_view_sensors_cursor_fila*MENU_VIEW_SENSORS_TOTAL_COLUMNS+menu_view_sensors_cursor_columna;

        menu_view_sensors_fondo_cursor(ventana,ESTILO_GUI_TINTA_SELECCIONADO,ESTILO_GUI_PAPEL_SELECCIONADO);


        tecla=zxvision_common_getkey_refresh();
        zxvision_handle_cursors_pgupdn(ventana,tecla);

        //printf("mouse_left: %d tecla: %d\n",mouse_left,tecla);


        //Si se pulsa boton y esta en rango
        if (mouse_left && tecla==0 && menu_view_sensors_mouse_in_zone_widgets(NULL,NULL) ) {
            //printf("pulsado left\n");
            //menu_espera_no_tecla();
            tecla=13;
        }

        //printf("mouse %d %d\n",menu_mouse_x,menu_mouse_y);

        //antes_mouse_left=mouse_left;

        //gestionar movimiento cursor
        //Solo si se ha movido
        if (antes_menu_mouse_x!=menu_mouse_x || antes_menu_mouse_y!=menu_mouse_y) {
            //printf("mouse movido\n");

            int cursor_mouse_x,cursor_mouse_y;

            if (menu_view_sensors_mouse_in_zone_widgets(&cursor_mouse_x,&cursor_mouse_y)) {
                //movemos cursor
                menu_view_sensors_cursor_columna=cursor_mouse_x;
                menu_view_sensors_cursor_fila=cursor_mouse_y;
            }


        }

        antes_menu_mouse_x=menu_mouse_x;
        antes_menu_mouse_y=menu_mouse_y;

        switch(tecla) {
            case 8:
                if (menu_view_sensors_cursor_columna>0) menu_view_sensors_cursor_columna--;
            break;

            case 9:
                if (menu_view_sensors_cursor_columna<MENU_VIEW_SENSORS_TOTAL_COLUMNS-1) menu_view_sensors_cursor_columna++;
            break;

            case 11:
                if (menu_view_sensors_cursor_fila>0) menu_view_sensors_cursor_fila--;
            break;

            case 10:
                if (menu_view_sensors_cursor_fila<MENU_VIEW_SENSORS_TOTAL_ROWS-1) menu_view_sensors_cursor_fila++;
            break;

            case 13:

                //necesario cuando viene de pulsar boton izquierdo con raton
                menu_espera_no_tecla();

                //buscar el id para ese nombre de sensor
                short_name=menu_debug_view_sensors_list_sensors[offset_array].short_name;
                int sensor_id;



                //estaba vacio. asignar
                if (short_name[0]==0) {
                    sensor_id=0;
                }
                else {
                    sensor_id=sensor_find(short_name);
                    sensor_id++;
                    //invalido
                    if (sensor_id<0) sensor_id=0;
                }

                sensor_id=menu_debug_view_sensors_get_sensor_item(sensor_id);

                if (sensor_id>=0) {
                    if (sensor_id==0) {
                        //vacio
                        strcpy(menu_debug_view_sensors_list_sensors[offset_array].short_name,"");
                    }
                    else {
                        //indice-1
                        sensor_id--;
                        short_name=sensors_array[sensor_id].short_name;
                        strcpy(menu_debug_view_sensors_list_sensors[offset_array].short_name,short_name);
                    }
                }

            break;

            case 't':
                menu_debug_view_sensors_tipo=menu_debug_view_sensors_list_sensors[offset_array].tipo;

                menu_debug_view_sensors_tipo=menu_debug_view_sensors_get_sensor_tipo(menu_debug_view_sensors_tipo);

                //menu_debug_view_sensors_tipo++;
                //if (menu_debug_view_sensors_tipo>=ZXVISION_TOTAL_WIDGET_TYPES) menu_debug_view_sensors_tipo=0;

                if (menu_debug_view_sensors_tipo>=0) menu_debug_view_sensors_list_sensors[offset_array].tipo=menu_debug_view_sensors_tipo;
            break;

            case 'a':
                menu_debug_view_sensors_list_sensors[offset_array].valor_en_vez_de_perc ^=1;
                if (menu_debug_view_sensors_list_sensors[offset_array].valor_en_vez_de_perc) {
                    menu_generic_message_splash("Display value","OK Showing absolute value instead of percentage");
                }
                else {
                    menu_generic_message_splash("Display percentage","OK Showing percentage instead of absolute value");
                }

            break;


        }
            //printf ("tecla: %d\n",tecla);
    } while (tecla!=2 && tecla!=3);



    //menu_debug_view_sensors_clear_cursor(ventana,menu_view_sensors_cursor_fila,menu_view_sensors_cursor_columna);
    menu_view_sensors_cursor_visible=0;

    //borrar fondo cursor
    menu_view_sensors_fondo_cursor(ventana,ESTILO_GUI_TINTA_NORMAL,ESTILO_GUI_PAPEL_NORMAL);



    util_add_window_geometry_compact(ventana);

    if (tecla==3) {
        zxvision_message_put_window_background();
    }

    else {
        zxvision_destroy_window(ventana);
    }


}

















//#define VISUALREALTAPE_X (menu_origin_x())
//#define VISUALREALTAPE_Y 4
#define VISUALREALTAPE_ANCHO 32
#define VISUALREALTAPE_ALTO 20





//Usado dentro del overlay de waveform, para mostrar dos veces por segundo el texto que average, etc
int menu_visual_realtape_valor_contador_segundo_anterior;


zxvision_window *menu_audio_visual_realtape_window;

//offsets donde empieza y acaba bloque actual
long long int menu_visual_realtape_bloque_posicion_inicio=-1;
long long int menu_visual_realtape_bloque_posicion_final=-1;

void menu_visual_realtape_overlay(void)
{

    //if (!zxvision_drawing_in_background) normal_overlay_texto_menu();

	char buffer_texto_medio[40];

	menu_speech_set_tecla_pulsada(); //Si no, envia continuamente todo ese texto a speech

    //si ventana minimizada, no ejecutar todo el codigo de overlay
    if (menu_audio_visual_realtape_window->is_minimized) return;

    //printf ("contador_segundo=%d\n",contador_segundo);


    int dibujar=0;

    //esto hara ejecutar esto 5 veces por segundo
    if ( ((contador_segundo%200) == 0 && menu_visual_realtape_valor_contador_segundo_anterior!=contador_segundo) || menu_multitarea==0) {
        dibujar=1;
    }

    //Con top speed, solo se dibuja 1 frame por segundo. En ese caso, hacemos que siempre dibuje cuando hay ese frame
    if (timer_condicion_top_speed()) {
        //printf("top speed\n");
        dibujar=1;
    }

	if (dibujar) {

        menu_visual_realtape_valor_contador_segundo_anterior=contador_segundo;
        //printf ("Refrescando. contador_segundo=%d\n",contador_segundo);

        //menu_speech_set_tecla_pulsada(); //Si no, envia continuamente todo ese texto a speech

        //borrar lo que haya
        zxvision_print_string_defaults_fillspc(menu_audio_visual_realtape_window,1,0,"");
        if (realtape_name!=NULL) {
            char nombre[PATH_MAX];
            util_get_file_no_directory(realtape_name,nombre);

            char string_realtape_shown[50];
            menu_tape_settings_trunc_name(nombre,string_realtape_shown,50);

            char buffer_linea[100];
            sprintf(buffer_linea,"Name: %s",string_realtape_shown);
            zxvision_print_string_defaults_fillspc(menu_audio_visual_realtape_window,1,0,buffer_linea);
        }

        char *tipos_cinta[]={
            "Type: Unknown",
            "Type: ZX Spectrum",
            "Type: ZX80",
            "Type: ZX81"
        };

        //por si acaso
        if (realtape_visual_detected_tape_type>3) {
            zxvision_print_string_defaults_fillspc(menu_audio_visual_realtape_window,1,1,"Error geting file type");
        }

        else {
            zxvision_print_string_defaults_fillspc(menu_audio_visual_realtape_window,1,1,tipos_cinta[realtape_visual_detected_tape_type]);
        }


        //Average, min, max
        int elapsed_seconds=realtape_get_elapsed_seconds();
        int total_seconds=realtape_get_total_seconds();

        char buffer_elapsed[10];
        char buffer_total[10];

        util_print_minutes_seconds(elapsed_seconds,buffer_elapsed);
        util_print_minutes_seconds(total_seconds,buffer_total);

        sprintf (buffer_texto_medio,"Elapsed: %s Total: %s",buffer_elapsed,buffer_total);

        zxvision_print_string_defaults_fillspc(menu_audio_visual_realtape_window,1,2,buffer_texto_medio);


        //de momento borrar esas lineas
        zxvision_print_string_defaults_fillspc(menu_audio_visual_realtape_window,1,3,"");
        zxvision_print_string_defaults_fillspc(menu_audio_visual_realtape_window,1,4,"");
        zxvision_print_string_defaults_fillspc(menu_audio_visual_realtape_window,1,5,"");

        //Ver el bloque que corresponde a esta posicion

        int i;

        for (i=0;visual_realtape_array_positions[i]!=-1;i++) {

            //en cuanto nuestra posicion es menor que la leida del array, retornamos la anterior
            //printf("posicion actual: %ld array: %ld\n",realtape_file_size_counter,visual_realtape_array_positions[i]);

            if (visual_realtape_array_positions[i]>realtape_file_size_counter || visual_realtape_array_positions[i]==-1) break;
        }

        //printf("posicion real tape: %d\n",i);


        if (i>0) {
            i--;

            int posicion_buscar=i;
            //printf("Bloque %i position marker: %ld\n",posicion_buscar,visual_realtape_array_positions[posicion_buscar]);

            menu_visual_realtape_bloque_posicion_inicio=visual_realtape_array_positions[posicion_buscar];
            menu_visual_realtape_bloque_posicion_final=visual_realtape_array_positions[posicion_buscar+1];

            //Ahora localizar ese bloque dentro del texto. Cada texto empieza con dos saltos de linea seguidos
            int conteo_bloque=-1;
            int encontrado=0;
            for (i=0;visual_realtape_textbrowse[i] && !encontrado;i++) {
                if (visual_realtape_textbrowse[i]=='\n' && visual_realtape_textbrowse[i+1]=='\n') {
                    conteo_bloque++;
                    if (conteo_bloque==posicion_buscar) encontrado=1;
                }
            }
            if (encontrado) {
                //ahora pasar ese texto a otro string
                char buf_texto_bloque[1024];
                int indice_destino=0;
                int encontrado_final=0;
                //saltar primer salto de linea
                i++;
                for (;visual_realtape_textbrowse[i] && !encontrado_final;i++) {
                    buf_texto_bloque[indice_destino++]=visual_realtape_textbrowse[i];
                    if (visual_realtape_textbrowse[i]=='\n' && visual_realtape_textbrowse[i+1]=='\n') {
                        encontrado_final=1;
                    }
                }

                buf_texto_bloque[indice_destino]=0;

                //Y escribirlo. Al menos en dos lineas
                char buffer_linea[1024];
                int linea=0;
                int i;
                indice_destino=0;

                //Troceado en lineas por cada "\n" del buffer.
                for (i=0;buf_texto_bloque[i];i++) {

                    buffer_linea[indice_destino]=buf_texto_bloque[i];
                    if (buf_texto_bloque[i]=='\n') {
                        buffer_linea[indice_destino]=0;
                        zxvision_print_string_defaults_fillspc(menu_audio_visual_realtape_window,1,3+linea,buffer_linea);

                        indice_destino=0;

                        linea++;
                        if (linea==3) break;
                    }
                    else {
                        indice_destino++;
                    }
                }

            }

        }

	}


//printf("refrescando\n");
	//Ancho variable segun el tamanyo de ventana
	int ancho=menu_audio_visual_realtape_window->visible_width-2;

	//Por si acaso, no vayamos a provocar alguna division por cero
	if (ancho<1) ancho=1;

	int alto;

    //lineas de separacion por encima de la onda de audio
	int lineas_cabecera=9;

	alto=menu_audio_visual_realtape_window->visible_height-lineas_cabecera-2;

	//Por si acaso, no vayamos a provocar alguna division por cero
	if (alto<1) alto=1;



	int xorigen=1;
	int yorigen;



	yorigen=lineas_cabecera;


	if (si_complete_video_driver() ) {
        ancho *=menu_char_width;
        alto *=8;
        xorigen *=menu_char_width;
        yorigen *=8;
	}



    int tamanyo_trozo=realtape_visual_total_used/ancho;

    //compensar. Para que quepa toda la forma de onda en ventana. Si no, seguramente siempre habria un trozo al final que no se veria
    int tamanyo_trozo_compensado=tamanyo_trozo+1;

    //posicion en la cinta real tape
    long long int total=realtape_file_size;
    long long int transcurrido=realtape_file_size_counter;

    int maximo_x_dibujar=realtape_visual_total_used/tamanyo_trozo_compensado;


    //evitar divisiones por cero
    if (total==0) total=1;
    long long int posicion_cinta_x=(transcurrido*maximo_x_dibujar)/total;

    int x=0;

    int minimo,maximo;

    minimo=maximo=128;

    int indice;

    //printf("bloque posicion inicio %ld final %ld\n",menu_visual_realtape_bloque_posicion_inicio,menu_visual_realtape_bloque_posicion_final);
    //printf("ajustado bloque posicion inicio %ld final %ld\n",
    //    (menu_visual_realtape_bloque_posicion_inicio*maximo_x_dibujar)/total,
    //    (menu_visual_realtape_bloque_posicion_final*maximo_x_dibujar)/total);

    for (indice=0;indice<realtape_visual_total_used;indice++) {

        z80_byte valor_leido_maximo,valor_leido_minimo;

        valor_leido_minimo=realtape_visual_data[indice][0];
        valor_leido_maximo=realtape_visual_data[indice][1];

        //acumulado=acumulado+byte_leido;
        if (valor_leido_minimo<minimo) minimo=valor_leido_minimo;
        if (valor_leido_maximo>maximo) maximo=valor_leido_maximo;


        if ((indice%tamanyo_trozo_compensado)==0) {
            //siguiente trozo

            //por si acaso controlar maximo
            if (x<ancho) {
                //escalar el alto total a lo que corresponda
                int ymin=(minimo*alto)/256;
                int ymax=(maximo*alto)/256;

                int color_fondo=ESTILO_GUI_PAPEL_NORMAL;

                //Ver si estamos dibujando zona que esta entre los margenes del bloque actual
                //menu_visual_realtape_bloque_posicion_inicio, menu_visual_realtape_bloque_posicion_final
                //Convertir umbrales a posicion x

                //importante que sean long it igual que menu_visual_realtape_bloque_posicion_inicio,
                //sino, luego al multiplicar mas abajo podemos salirnos de rango facilmente
                long long int umbral_min=menu_visual_realtape_bloque_posicion_inicio;
                long long int umbral_max=menu_visual_realtape_bloque_posicion_final;

                if (umbral_min!=-1) {
                    umbral_min=(umbral_min*maximo_x_dibujar)/total;
                }

                if (umbral_max!=-1) {
                    umbral_max=(umbral_max*maximo_x_dibujar)/total;
                }


                //printf("cursor %d umbrales %d %d\n",x,umbral_min,umbral_max);
                //printf("xxx %ld\n",realtape_visual_total_used*tamanyo_trozo_compensado);

                if (umbral_min!=-1) {
                    if (x>=umbral_min) {
                        if (umbral_max==-1 ||  //Esto indica que no hay siguiente bloque
                                x < umbral_max) {

                            //printf("cambio color x: %d min %ld max %ld\n",x,umbral_min,umbral_max);

                            color_fondo=ESTILO_GUI_COLOR_BLOCK_VISUALTAPE;
                        }
                    }
                }


                int y;

                //0,0 es arriba del todo
                //dado que empezamos dibujando hasta ymin y luego hasta ymax, queremos que nuestro ymin se vea abajo del todo
                //por eso restamos siempre alto-

                for (y=0;y<ymin;y++) {
                    zxvision_putpixel(menu_audio_visual_realtape_window,x+xorigen,(alto-1-y)+yorigen,color_fondo);
                }

                //condicion <= porque hay que llegar hasta el valor maximo
                for (;y<=ymax;y++) {
                    zxvision_putpixel(menu_audio_visual_realtape_window,x+xorigen,(alto-1-y)+yorigen,ESTILO_GUI_COLOR_WAVEFORM);
                }

                for (;y<alto;y++) {
                    zxvision_putpixel(menu_audio_visual_realtape_window,x+xorigen,(alto-1-y)+yorigen,color_fondo);
                }

                //Si esta el cursor de cinta aqui, dibujar linea vertical
                if (x==posicion_cinta_x) {
                    for (y=0;y<alto;y++) {
                        zxvision_putpixel(menu_audio_visual_realtape_window,x+xorigen,y+yorigen,ESTILO_GUI_COLOR_AVISO);
                    }
                }

                x++;
            }
            else {
                //printf("Trying to write pixel beyond realtape visual: %d\n",x);
                return;
            }

            minimo=maximo=128;

        }
    }


	zxvision_draw_window_contents(menu_audio_visual_realtape_window);

}


//Esto utilizado en ventana de Visual Casette tape, no de Visual real tape
int visual_cassette_tape_forzar_dibujado=0;



zxvision_window zxvision_window_visual_realtape;

void menu_visual_realtape_rewind(MENU_ITEM_PARAMETERS)
{
    realtape_rewind_five();
    //Para hacer que la ventana de Visual Casette Tape refresque bien, si es que esta abierta
    visual_cassette_tape_forzar_dibujado=1;
}

void menu_visual_realtape_ffwd(MENU_ITEM_PARAMETERS)
{
    realtape_ffwd_five();
    //Para hacer que la ventana de Visual Casette Tape refresque bien, si es que esta abierta
    visual_cassette_tape_forzar_dibujado=1;

}

void menu_visual_realtape_rewind_one(MENU_ITEM_PARAMETERS)
{
    realtape_rewind_one();
    //Para hacer que la ventana de Visual Casette Tape refresque bien, si es que esta abierta
    visual_cassette_tape_forzar_dibujado=1;

}

void menu_visual_realtape_ffwd_one(MENU_ITEM_PARAMETERS)
{
    realtape_ffwd_one();
    //Para hacer que la ventana de Visual Casette Tape refresque bien, si es que esta abierta
    visual_cassette_tape_forzar_dibujado=1;

}

void menu_visual_realtape_reinsert(MENU_ITEM_PARAMETERS)
{
    if (!menu_realtape_cond() ) {
        debug_printf(VERBOSE_ERR,"There is no real tape selected");
        return;
    }
    menu_reinsert_real_tape();
}





void menu_visual_realtape_insert(MENU_ITEM_PARAMETERS)
{
    menu_realtape_open(0);

    //reestablecer overlay por que al llamar a menu_realtape_open, en el filesel, se resetea funcion overlay
    //set_menu_overlay_function(menu_visual_realtape_overlay);
}

void menu_visual_realtape_stopbegin(MENU_ITEM_PARAMETERS)
{

    realtape_rewind_begin();
    realtape_stop_playing();
}


void menu_visual_realtape(MENU_ITEM_PARAMETERS)
{

    menu_espera_no_tecla();
    menu_reset_counters_tecla_repeticion();


    zxvision_window *ventana;
    ventana=&zxvision_window_visual_realtape;


	//IMPORTANTE! no crear ventana si ya existe. Esto hay que hacerlo en todas las ventanas que permiten background.
	//si no se hiciera, se crearia la misma ventana, y en la lista de ventanas activas , al redibujarse,
	//la primera ventana repetida apuntaria a la segunda, que es el mismo puntero, y redibujaria la misma, y se quedaria en bucle colgado
	//zxvision_delete_window_if_exists(ventana);

    //Crear ventana si no existe
    if (!zxvision_if_window_already_exists(ventana)) {

        int x,y,ancho,alto,is_minimized,is_maximized,ancho_antes_minimize,alto_antes_minimize;

        if (!util_find_window_geometry("visualrealtape",&x,&y,&ancho,&alto,&is_minimized,&is_maximized,&ancho_antes_minimize,&alto_antes_minimize)) {
            //x=VISUALREALTAPE_X;
            //y=VISUALREALTAPE_Y;
            ancho=VISUALREALTAPE_ANCHO;
            alto=VISUALREALTAPE_ALTO+4;
            x=menu_center_x()-ancho/2;
            y=menu_center_y()-alto/2;
        }


        //zxvision_new_window_nocheck_staticsize(ventana,x,y,ancho,alto,ancho-1,alto-2,"Visual Real Tape");
        zxvision_new_window_gn_cim(ventana,x,y,ancho,alto,ancho-1,alto-2,"Visual Real Tape","visualrealtape",is_minimized,is_maximized,ancho_antes_minimize,alto_antes_minimize);

        ventana->can_be_backgrounded=1;
        //indicar nombre del grabado de geometria
        //strcpy(ventana->geometry_name,"visualrealtape");
        //restaurar estado minimizado de ventana
        //ventana->is_minimized=is_minimized;

        //printf("despues zxvision_new_window_nocheck_staticsize\n");

    }

    //Si ya existe, activar esta ventana
    else {

        zxvision_activate_this_window(ventana);
    }

	zxvision_draw_window(ventana);

    menu_audio_visual_realtape_window=ventana; //Decimos que el overlay lo hace sobre la ventana que tenemos aqui

    //Cambiamos funcion overlay de texto de menu
    //TODO: esta es un tanto peculiar porque al insertar cinta se cambia overlay??
	//set_menu_overlay_function(menu_visual_realtape_overlay);
    //cambio overlay
    zxvision_set_window_overlay(ventana,menu_visual_realtape_overlay);



	//Toda ventana que este listada en zxvision_known_window_names_array debe permitir poder salir desde aqui
	//Se sale despues de haber inicializado overlay y de cualquier otra variable que necesite el overlay
	if (zxvision_currently_restoring_windows_on_start) {
		//printf ("Saliendo de ventana ya que la estamos restaurando en startup\n");
		return;
	}

	menu_item *array_menu_common;
	menu_item item_seleccionado;
	int retorno_menu;
	do {

        //borrar linea de menu por si hay restos de insertar
        zxvision_print_string_defaults_fillspc(ventana,1,6,"");
        zxvision_print_string_defaults_fillspc(ventana,1,7,"");



        menu_add_item_menu_inicial(&array_menu_common,"",MENU_OPCION_UNASSIGNED,NULL,NULL);

        if (realtape_inserted.v) {

            menu_add_item_menu_format(array_menu_common,MENU_OPCION_NORMAL,menu_visual_realtape_rewind,NULL,"~~Rew");
            menu_add_item_menu_shortcut(array_menu_common,'r');
            menu_add_item_menu_tabulado(array_menu_common,1,6);

            menu_add_item_menu_format(array_menu_common,MENU_OPCION_NORMAL,menu_visual_realtape_rewind_one,NULL,"Rew 1%%");
            menu_add_item_menu_tabulado(array_menu_common,5,6);


            char string_playpause[32];
            if (realtape_playing.v) strcpy(string_playpause,"~~Pause");
            else strcpy(string_playpause,"~~Play ");
            menu_add_item_menu_format(array_menu_common,MENU_OPCION_NORMAL,menu_realtape_pause_unpause,NULL,string_playpause);
            menu_add_item_menu_shortcut(array_menu_common,'p');
            menu_add_item_menu_tabulado(array_menu_common,12,6);

            menu_add_item_menu_format(array_menu_common,MENU_OPCION_NORMAL,menu_visual_realtape_ffwd_one,NULL,"FF 1%%");
            menu_add_item_menu_tabulado(array_menu_common,18,6);

            menu_add_item_menu_format(array_menu_common,MENU_OPCION_NORMAL,menu_visual_realtape_ffwd,NULL,"~~FF");
            menu_add_item_menu_shortcut(array_menu_common,'f');
            menu_add_item_menu_tabulado(array_menu_common,24,6);



        }

        if (menu_realtape_cond() ) {
		    menu_add_item_menu_format(array_menu_common,MENU_OPCION_NORMAL,menu_visual_realtape_reinsert,NULL,"R~~einsert");
		    menu_add_item_menu_shortcut(array_menu_common,'e');
		    menu_add_item_menu_tabulado(array_menu_common,1,7);
        }

		menu_add_item_menu_format(array_menu_common,MENU_OPCION_NORMAL,menu_visual_realtape_insert,NULL,"~~Insert");
		menu_add_item_menu_shortcut(array_menu_common,'i');
		menu_add_item_menu_tabulado(array_menu_common,10,7);

        if (menu_realtape_cond() && realtape_playing.v) {
		    menu_add_item_menu_format(array_menu_common,MENU_OPCION_NORMAL,menu_visual_realtape_stopbegin,NULL,"StopBegin");
		    //menu_add_item_menu_shortcut(array_menu_common,'s');
		    menu_add_item_menu_tabulado(array_menu_common,17,7);
        }


		//Nombre de ventana solo aparece en el caso de stdout
		retorno_menu=menu_dibuja_menu_no_title_lang(&audio_visual_realtape_opcion_seleccionada,&item_seleccionado,array_menu_common,"Visual Real Tape" );

		if (retorno_menu!=MENU_RETORNO_BACKGROUND) {

			//En caso de menus tabulados, es responsabilidad de este de borrar la ventana

			if ((item_seleccionado.tipo_opcion&MENU_OPCION_ESC)==0 && retorno_menu>=0) {
				//llamamos por valor de funcion
				if (item_seleccionado.menu_funcion!=NULL) {
					//printf ("actuamos por funcion\n");
					item_seleccionado.menu_funcion(item_seleccionado.valor_opcion);

				}
			}
		}

	} while ( (item_seleccionado.tipo_opcion&MENU_OPCION_ESC)==0 && retorno_menu!=MENU_RETORNO_ESC && !salir_todos_menus && retorno_menu!=MENU_RETORNO_BACKGROUND);


    //En caso de menus tabulados, suele ser necesario esto. Si no, la ventana se quedaria visible


    //Grabar geometria ventana
    util_add_window_geometry_compact(ventana);


    if (retorno_menu==MENU_RETORNO_BACKGROUND) {
        zxvision_message_put_window_background();
    }

    else {
        //En caso de menus tabulados, es responsabilidad de este de liberar ventana
        zxvision_destroy_window(ventana);
    }

}


void hotswap_zxuno_to_p2as_set_pages(void)
{

                int i;
                //Punteros a paginas de la ROM
                for (i=0;i<4;i++) {
                        rom_mem_table[i]=zxuno_sram_mem_table_new[i+8];
                }

                //Punteros a paginas de la RAM
                for (i=0;i<8;i++) {
                        ram_mem_table[i]=zxuno_sram_mem_table_new[i];
                }


                //Paginas mapeadas actuales
                for (i=0;i<4;i++) {
                        memory_paged[i]=zxuno_memory_paged_brandnew[i*2];
                }
}


//Hace hotswap de cualquier maquina a spectrum 48. simplemente hace copia de los 64kb
/*NOTA: no uso esta funcion pues al pasar de zxuno a spectrum
la pantalla refresca "de otro sitio", y solo se puede ver si se habilita interfaz spectra...
es raro, es un error pero no tiene que ver con spectra, tiene que ver con divmmc:
-si hago hotswap de zxuno a 48k con divmmc, luego la pantalla no se refresca
-si hago hotswap de zxuno a 48k sin divmmc, si se ve bien
No se exactamente porque ocurre, seguramente algo que ver con peek_byte_no_time y paginas de divmmc
Uso la otra funcion de hotswap_any_machine_to_spec48 que hay mas abajo
*/
void to_delete_hotswap_any_machine_to_spec48(void)
{

	//Asignamos 64kb RAM
	z80_byte *memoria_spectrum_final;
	memoria_spectrum_final=malloc(65536);

	if (memoria_spectrum_final==NULL) {
		cpu_panic ("Error. Cannot allocate Machine memory");
	}

        //Copiamos ROM y RAM a destino
        int i;
        //ROM y RAM
        for (i=0;i<65536;i++) memoria_spectrum_final[i]=peek_byte_no_time(i);

	free(memoria_spectrum);
        memoria_spectrum=memoria_spectrum_final;

	current_machine_type=1;

        set_machine_params();
        post_set_machine(NULL);

}

void hotswap_any_machine_to_spec48(void)
{

        //Asignamos 64kb RAM
        z80_byte *memoria_buffer;
        memoria_buffer=malloc(65536);

        if (memoria_buffer==NULL) {
                cpu_panic ("Error. Cannot allocate Machine memory");
        }

        //Copiamos ROM y RAM en buffer
        int i;
        for (i=0;i<65536;i++) memoria_buffer[i]=peek_byte_no_time(i);

        current_machine_type=MACHINE_ID_SPECTRUM_48;

        set_machine(NULL);

        //Copiar contenido de buffer en ROM y RAM
	//Por tanto la rom de destino sera la que habia antes del hotswap
	memcpy(memoria_spectrum,memoria_buffer,65536);

        free(memoria_buffer);
}

void hotswap_p2a_to_128(void)
{
	//Copiamos ROM0 y ROM3 en ROM 0 y 1 de spectrum 128k
	//La ram la copiamos tal cual
	z80_byte old_puerto_32765=puerto_32765;

	//Asignamos 32+128k de memoria
	z80_byte *memoria_buffer;
	memoria_buffer=malloc((32+128)*1024);

	if (memoria_buffer==NULL) {
					cpu_panic ("Error. Cannot allocate Machine memory");
	}

	//Copiamos rom 0 en buffer
	int i;
	z80_byte *puntero;
	puntero=rom_mem_table[0];
	for (i=0;i<16384;i++) {
		memoria_buffer[i]=*puntero;
		puntero++;
	}

	//Copiamos rom 3 en buffer
	puntero=rom_mem_table[3];
	for (i=0;i<16384;i++) {
		memoria_buffer[16384+i]=*puntero;
		puntero++;
	}

	//Copiamos paginas de ram
	int pagina;
	for (pagina=0;pagina<8;pagina++) {
		puntero=ram_mem_table[pagina];
		for (i=0;i<16384;i++) {
			memoria_buffer[32768+pagina*16384+i]=*puntero;
			puntero++;
		}
	}

	//Spectrum 128k
    current_machine_type=MACHINE_ID_SPECTRUM_128;

        set_machine(NULL);

 //Mapear como estaba
 puerto_32765=old_puerto_32765;

	//asignar ram
	mem_page_ram_128k();

	//asignar rom
	mem_page_rom_128k();

	//Copiar contenido de buffer en ROM y RAM
	//No nos complicamos la vida, como sabemos que viene lineal las dos roms y la ram, volcamos

	for (i=0;i<(32+128)*1024;i++) memoria_spectrum[i]=memoria_buffer[i];

  free(memoria_buffer);



}


//maquina: tal cual el id de maquina
void hotswap_128_to_p2a_p3(int maquina)
{
	//Copiamos ROM0 en ROM0, ROM1 en ROM1, ROM0 en ROM2 y ROM1 en ROM3 de +2a
	//La ram la copiamos tal cual
	z80_byte old_puerto_32765=puerto_32765;

	//Asignamos 64+128k de memoria
	z80_byte *memoria_buffer;
	memoria_buffer=malloc((64+128)*1024);

	if (memoria_buffer==NULL) {
					cpu_panic ("Error. Cannot allocate Machine memory");
	}

	//Copiamos rom 0 y en buffer
	int i;
	z80_byte *puntero;
	puntero=rom_mem_table[0];
	for (i=0;i<16384;i++) {
		memoria_buffer[i]=*puntero;
		memoria_buffer[32768+i]=*puntero;
		puntero++;
	}

	//Copiamos rom 3 en buffer
	puntero=rom_mem_table[1];
	for (i=0;i<16384;i++) {
		memoria_buffer[16384+i]=*puntero;
		memoria_buffer[49152+i]=*puntero;
		puntero++;
	}

	//Copiamos paginas de ram
	int pagina;
	for (pagina=0;pagina<8;pagina++) {
		puntero=ram_mem_table[pagina];
		for (i=0;i<16384;i++) {
			memoria_buffer[65536+pagina*16384+i]=*puntero;
			puntero++;
		}
	}

	//Spectrum +2A
        //current_machine_type=MACHINE_ID_SPECTRUM_P2A_40;
        current_machine_type=maquina;

        set_machine(NULL);

 //Mapear como estaba
 puerto_32765=old_puerto_32765;

 puerto_8189=0;

	//asignar ram
	mem_page_ram_p2a();

	//asignar rom
	mem_page_rom_p2a();

	//Copiar contenido de buffer en ROM y RAM
	//No nos complicamos la vida, como sabemos que viene lineal las  roms y la ram, volcamos

	for (i=0;i<(64+128)*1024;i++) memoria_spectrum[i]=memoria_buffer[i];

  free(memoria_buffer);



}


//Hace hotswap de cualquier maquina 48 a spectrum 128. Se guarda los 48kb de ram en un buffer, cambia maquina, y vuelca contenido ram
void hotswap_any_machine_to_spec128(void)
{

        //Asignamos 48kb RAM
        z80_byte *memoria_buffer;
        memoria_buffer=malloc(49152);

        if (memoria_buffer==NULL) {
                cpu_panic ("Error. Cannot allocate Machine memory");
        }

        //Copiamos RAM en buffer
        int i;
        for (i=0;i<49152;i++) memoria_buffer[i]=peek_byte_no_time(16384+i);

	//Spectrum 128k
        current_machine_type=MACHINE_ID_SPECTRUM_128;

        set_machine(NULL);

	//Paginar ROM 1 y RAM 0
	puerto_32765=16;

	//asignar ram
	mem_page_ram_128k();

	//asignar rom
	mem_page_rom_128k();

	//Copiar contenido de buffer en RAM
	for (i=0;i<49152;i++) poke_byte_no_time(16384+i,memoria_buffer[i]);

        free(memoria_buffer);
}



void hotswap_pcw_to_8256(MENU_ITEM_PARAMETERS)
{
    current_machine_type=MACHINE_ID_PCW_8256;
    pcw_total_ram=256*1024;
    //No hay mas diferencias entre las dos
}

void hotswap_pcw_to_8512(MENU_ITEM_PARAMETERS)
{
    current_machine_type=MACHINE_ID_PCW_8512;
    pcw_total_ram=512*1024;
    //No hay mas diferencias entre las dos
}


void hotswap_cpc_to_464(MENU_ITEM_PARAMETERS)
{
    current_machine_type=MACHINE_ID_CPC_464;
    set_machine_params();
    post_set_machine(NULL);
}

void hotswap_cpc_to_4128(MENU_ITEM_PARAMETERS)
{
    current_machine_type=MACHINE_ID_CPC_4128;
    set_machine_params();
    post_set_machine(NULL);
}

void hotswap_cpc_to_664(MENU_ITEM_PARAMETERS)
{
    current_machine_type=MACHINE_ID_CPC_664;
    set_machine_params();
    post_set_machine(NULL);
}

void hotswap_cpc_to_6128(MENU_ITEM_PARAMETERS)
{
    current_machine_type=MACHINE_ID_CPC_6128;
    set_machine_params();
    post_set_machine(NULL);
}

/*
void hotswap_zx8081_common(void)
{

    set_machine_params();
    post_set_machine(NULL);

    //ajustar algunos registros
    if (MACHINE_IS_ZX80_TYPE) reg_i=0x0E;

    if (MACHINE_IS_ZX81_TYPE) {
        reg_i=0x1E;
        nmi_generator_active.v=0;
    }


}
*/

/*
void hotswap_zx8081_to_zx80(MENU_ITEM_PARAMETERS)
{
    current_machine_type=MACHINE_ID_ZX80;
    hotswap_zx8081_common();
}
*/

/*
void hotswap_zx8081_to_zx81(MENU_ITEM_PARAMETERS)
{
    current_machine_type=MACHINE_ID_ZX81;
    hotswap_zx8081_common();
}
*/

void hotswap_zx8080_to_zx8081_common(int target_machine)
{

    //Copiaremos el contenido de la rom+ram en la maquina de destino

    z80_byte *memoria_buffer;
    memoria_buffer=util_malloc(65536,"Can not allocate memory for hotswap");

    int i;
    for (i=0;i<65536;i++) memoria_buffer[i]=memoria_spectrum[i];

    current_machine_type=target_machine;
    set_machine(NULL);

    //Restaurar rom+ram
    for (i=0;i<65536;i++) memoria_spectrum[i]=memoria_buffer[i];

    free(memoria_buffer);




}

void hotswap_zx80_to_zx81(MENU_ITEM_PARAMETERS)
{
    hotswap_zx8080_to_zx8081_common(MACHINE_ID_ZX81);

    menu_warn_message("Note that ROM data are the same as the previous machine");

}

void hotswap_zx81_to_zx80(MENU_ITEM_PARAMETERS)
{




    //pasar a modo fast, desde modo fast si que hace hotswap a zx80
    //call_address(0x02e7);

    //EI
    /*iff1.v=iff2.v=1;

    push_valor(reg_pc,PUSH_VALUE_TYPE_CALL);
    reg_pc=0x02e7;



    int i;
    for (i=0;i<100000;i++) cpu_core_loop();

    printf("nmi_generator_active.v: %d hsync_generator_active.v: %d\n",nmi_generator_active.v,hsync_generator_active.v);
    */



    hotswap_zx8080_to_zx8081_common(MACHINE_ID_ZX80);

    hotswapped_from_zx81=1;

    menu_warn_message("Note that ROM data are the same as the previous machine");


    if (memoria_spectrum[16443] & 128) {
        menu_warn_message("Seems you were in SLOW mode. Probably you need to set FAST mode before hotswap to avoid hang");
    }

    if (rainbow_enabled.v) menu_warn_message("You probably need to disable realvideo");



    return;


    nmi_generator_active.v=0;
    //res 7,(cdflag)
    //16443
    memoria_spectrum[16443] &=127;

}


void hotswap_resto_a_48k(MENU_ITEM_PARAMETERS)
{
    hotswap_any_machine_to_spec48();
}




void hotswap_p2a_to_p2a40(MENU_ITEM_PARAMETERS)
{
    current_machine_type=MACHINE_ID_SPECTRUM_P2A_40;
    set_machine_params();
    post_set_machine(NULL);
}

void hotswap_p2a_to_p2a41(MENU_ITEM_PARAMETERS)
{
    current_machine_type=MACHINE_ID_SPECTRUM_P2A_41;
    set_machine_params();
    post_set_machine(NULL);
}

void hotswap_p2a_to_p2aspa(MENU_ITEM_PARAMETERS)
{
    current_machine_type=MACHINE_ID_SPECTRUM_P2A_SPA;
    set_machine_params();
    post_set_machine(NULL);
}

void hotswap_p2a_to_p3a40(MENU_ITEM_PARAMETERS)
{
    current_machine_type=MACHINE_ID_SPECTRUM_P3_40;
    set_machine_params();
    post_set_machine(NULL);
}

void hotswap_p2a_to_p3a41(MENU_ITEM_PARAMETERS)
{
    current_machine_type=MACHINE_ID_SPECTRUM_P3_41;
    set_machine_params();
    post_set_machine(NULL);
}

void hotswap_p2a_to_p3aspa(MENU_ITEM_PARAMETERS)
{
    current_machine_type=MACHINE_ID_SPECTRUM_P3_SPA;
    set_machine_params();
    post_set_machine(NULL);
}

void hotswap_p2a_to_128k(MENU_ITEM_PARAMETERS)
{
    hotswap_p2a_to_128();
    menu_warn_message("Note that ROM data are the previous data coming from +2A");
}

void hotswap_p2a_to_48k(MENU_ITEM_PARAMETERS)
{
    hotswap_any_machine_to_spec48();
}



void hotswap_128k_to_128k(MENU_ITEM_PARAMETERS)
{
    current_machine_type=MACHINE_ID_SPECTRUM_128;
    set_machine_params();
    post_set_machine(NULL);
}

void hotswap_128k_to_pentagon(MENU_ITEM_PARAMETERS)
{
    current_machine_type=MACHINE_ID_PENTAGON;
    set_machine_params();
    post_set_machine(NULL);
}

void hotswap_128k_to_128k_spa(MENU_ITEM_PARAMETERS)
{
    current_machine_type=MACHINE_ID_SPECTRUM_128_SPA;
    set_machine_params();
    post_set_machine(NULL);
}

void hotswap_128k_to_p2(MENU_ITEM_PARAMETERS)
{
    current_machine_type=MACHINE_ID_SPECTRUM_P2;
    set_machine_params();
    post_set_machine(NULL);
}

void hotswap_128k_to_p2f(MENU_ITEM_PARAMETERS)
{
    current_machine_type=MACHINE_ID_SPECTRUM_P2_FRE;
    set_machine_params();
    post_set_machine(NULL);
}

void hotswap_128k_to_p2_spa(MENU_ITEM_PARAMETERS)
{
    current_machine_type=MACHINE_ID_SPECTRUM_P2_SPA;
    set_machine_params();
    post_set_machine(NULL);
}

void hotswap_128k_to_p2a(MENU_ITEM_PARAMETERS)
{
    hotswap_128_to_p2a_p3(MACHINE_ID_SPECTRUM_P2A_40);
    menu_warn_message("Note that ROM data are the previous data coming from 128K");
}

void hotswap_128k_to_p3(MENU_ITEM_PARAMETERS)
{
    hotswap_128_to_p2a_p3(MACHINE_ID_SPECTRUM_P3_40);
    menu_warn_message("Note that ROM data are the previous data coming from 128K");
}

void hotswap_128k_to_48k(MENU_ITEM_PARAMETERS)
{
    hotswap_any_machine_to_spec48();
}


void hotswap_chloe_to_140(MENU_ITEM_PARAMETERS)
{
    current_machine_type=MACHINE_ID_CHLOE_140SE;
    set_machine_params();
    post_set_machine(NULL);
}

void hotswap_chloe_to_280(MENU_ITEM_PARAMETERS)
{
    current_machine_type=MACHINE_ID_CHLOE_280SE;
    set_machine_params();
    post_set_machine(NULL);
}

void hotswap_chloe_to_48k(MENU_ITEM_PARAMETERS)
{
    hotswap_any_machine_to_spec48();
}


void hotswap_zxuno_to_p2a(MENU_ITEM_PARAMETERS)
{

    current_machine_type=MACHINE_ID_SPECTRUM_P2A_40;
    set_machine_params();

    //no cargar rom, la rom sera la que haya activa en las paginas del zxuno
    post_set_machine_no_rom_load();

    //dejamos toda la memoria que hay asignada del zx-uno, solo que
    //reasignamos los punteros de paginacion del +2a

    hotswap_zxuno_to_p2as_set_pages();


    //Si teniamos el divmmc activo. Llamar a esto manualmente, no a todo divmmc_enable(),
    //pues cargaria por ejemplo el firmware esxdos de disco, y mejor conservamos el mismo firmware
    //que haya cargado el ZX-Uno
    if (diviface_enabled.v) {
        diviface_set_peek_poke_functions();
        //diviface_paginacion_manual_activa.v=0;
        diviface_control_register&=(255-128);
        diviface_paginacion_automatica_activa.v=0;
    }

    menu_warn_message("Note that ROM data are the previous data coming from ZX-Uno");

}

void hotswap_zxuno_to_48k(MENU_ITEM_PARAMETERS)
{
    hotswap_any_machine_to_spec48();
}

void hotswap_1648_to_1648(MENU_ITEM_PARAMETERS)
{

    //Caso especial cuando se cambia entre maquina Inves, porque la asignacion de memoria es diferente
    if (MACHINE_IS_INVES || valor_opcion==4) {
        //si misma maquina inves origen o destino, no hacer nada

        //Cambiamos de Inves a otra
        if (MACHINE_IS_INVES && valor_opcion!=4) {
            //Asignamos 64kb RAM
            z80_byte *memoria_spectrum_final;
                memoria_spectrum_final=malloc(65536);

                if (memoria_spectrum_final==NULL) {
                    cpu_panic ("Error. Cannot allocate Machine memory");
                }

            //Copiamos ROM y RAM a destino
            int i;
            //ROM
            for (i=0;i<16384;i++) memoria_spectrum_final[i]=memoria_spectrum[65536+i];

            //RAM
            for (i=16384;i<65536;i++) memoria_spectrum_final[i]=memoria_spectrum[i];

            free(memoria_spectrum);
            memoria_spectrum=memoria_spectrum_final;

        }

        //Cambiamos de otra a Inves
        if (!(MACHINE_IS_INVES) && valor_opcion==4) {
            //Asignamos 80 kb RAM
            z80_byte *memoria_spectrum_final;
            memoria_spectrum_final=malloc(65536+16384);

            if (memoria_spectrum_final==NULL) {
                    cpu_panic ("Error. Cannot allocate Machine memory");
            }

            //Copiamos ROM y RAM a destino
            int i;
            //ROM
            for (i=0;i<16384;i++) memoria_spectrum_final[65536+i]=memoria_spectrum[i];

            //RAM
            for (i=16384;i<65536;i++) memoria_spectrum_final[i]=memoria_spectrum[i];

            free(memoria_spectrum);
            memoria_spectrum=memoria_spectrum_final;

            //Establecemos valores de low ram inves
            random_ram_inves(memoria_spectrum,16384);


        }
    }

    switch(valor_opcion)
    {
        case 0:
            current_machine_type=MACHINE_ID_SPECTRUM_16;
        break;

        case 1:
            current_machine_type=MACHINE_ID_SPECTRUM_48;
        break;

        case 2:
            current_machine_type=MACHINE_ID_SPECTRUM_48_PLUS_ENG;
        break;

        case 3:
            current_machine_type=MACHINE_ID_SPECTRUM_48_PLUS_SPA;
        break;

        case 4:
            current_machine_type=MACHINE_ID_INVES;
        break;

        case 5:
            current_machine_type=MACHINE_ID_MICRODIGITAL_TK90X;
        break;

        case 6:
            current_machine_type=MACHINE_ID_MICRODIGITAL_TK90X_SPA;
        break;

        case 7:
            current_machine_type=MACHINE_ID_MICRODIGITAL_TK95;
        break;

        case 8:
            current_machine_type=MACHINE_ID_MICRODIGITAL_TK95_SPA;
        break;

        case 9:
            current_machine_type=MACHINE_ID_TIMEX_TC2048;
		break;


    }

    set_machine_params();
    post_set_machine(NULL);


}

void hotswap_1648_to_128k(MENU_ITEM_PARAMETERS)
{
    hotswap_any_machine_to_spec128();
}

void hotswap_timex2068_to_tc2068(MENU_ITEM_PARAMETERS)
{
    current_machine_type=MACHINE_ID_TIMEX_TC2068;

    set_machine_params();
    post_set_machine(NULL);
}

void hotswap_timex2068_to_ts2068(MENU_ITEM_PARAMETERS)
{
    current_machine_type=MACHINE_ID_TIMEX_TS2068;

    set_machine_params();
    post_set_machine(NULL);
}

void hotswap_zx80_to_zx80(MENU_ITEM_PARAMETERS)
{


    switch(valor_opcion)
    {
        case 0:
            current_machine_type=MACHINE_ID_MICRODIGITAL_TK80;
        break;

        case 1:
            current_machine_type=MACHINE_ID_MICRODIGITAL_TK82;
        break;

        case 2:
            current_machine_type=MACHINE_ID_ZX80;
        break;


    }

	//maquinas las emulo igual, ni cambia la rom
    //set_machine_params();
    //post_set_machine(NULL);


}


void hotswap_zx81_to_zx81(MENU_ITEM_PARAMETERS)
{

    switch(valor_opcion)
    {
        case 0:
            current_machine_type=MACHINE_ID_TIMEX_TS1000;
        break;

        case 1:
            current_machine_type=MACHINE_ID_TIMEX_TS1500;
        break;

        case 2:
            current_machine_type=MACHINE_ID_MICRODIGITAL_TK82C;
        break;

        case 3:
            current_machine_type=MACHINE_ID_MICRODIGITAL_TK83;
        break;

        case 4:
            current_machine_type=MACHINE_ID_MICRODIGITAL_TK85;
        break;

        case 5:
            current_machine_type=MACHINE_ID_ZX81;
        break;


    }

	//maquinas las emulo igual, por tanto solo cambia la rom
    //set_machine_params();
    post_set_machine(NULL);


}


void menu_hotswap_machine(MENU_ITEM_PARAMETERS)
{

    menu_item *array_menu_machine_selection;
    menu_item item_seleccionado;
    int retorno_menu;

    do {

        hotswap_machine_opcion_seleccionada=0;

        //casos maquinas 16k, 48k
        if (MACHINE_IS_SPECTRUM_16_48) {
            menu_add_item_menu_inicial(&array_menu_machine_selection,"ZX Spectrum 16k",MENU_OPCION_NORMAL,hotswap_1648_to_1648,NULL);
            menu_add_item_menu_valor_opcion(array_menu_machine_selection,0);
            menu_add_item_menu(array_menu_machine_selection,"ZX Spectrum 48k",MENU_OPCION_NORMAL,hotswap_1648_to_1648,NULL);
            menu_add_item_menu_valor_opcion(array_menu_machine_selection,1);
            menu_add_item_menu(array_menu_machine_selection,"ZX Spectrum+ 48k",MENU_OPCION_NORMAL,hotswap_1648_to_1648,NULL);
            menu_add_item_menu_valor_opcion(array_menu_machine_selection,2);
            menu_add_item_menu(array_menu_machine_selection,"ZX Spectrum+ 48k (Spanish)",MENU_OPCION_NORMAL,hotswap_1648_to_1648,NULL);
            menu_add_item_menu_valor_opcion(array_menu_machine_selection,3);
            menu_add_item_menu(array_menu_machine_selection,"Inves Spectrum +",MENU_OPCION_NORMAL,hotswap_1648_to_1648,NULL);
            menu_add_item_menu_valor_opcion(array_menu_machine_selection,4);
            menu_add_item_menu(array_menu_machine_selection,"Microdigital TK90X",MENU_OPCION_NORMAL,hotswap_1648_to_1648,NULL);
            menu_add_item_menu_valor_opcion(array_menu_machine_selection,5);
            menu_add_item_menu(array_menu_machine_selection,"Microdigital TK90X (Spanish)",MENU_OPCION_NORMAL,hotswap_1648_to_1648,NULL);
            menu_add_item_menu_valor_opcion(array_menu_machine_selection,6);
            menu_add_item_menu(array_menu_machine_selection,"Microdigital TK95",MENU_OPCION_NORMAL,hotswap_1648_to_1648,NULL);
            menu_add_item_menu_valor_opcion(array_menu_machine_selection,7);
            menu_add_item_menu(array_menu_machine_selection,"Microdigital TK95 (Spanish)",MENU_OPCION_NORMAL,hotswap_1648_to_1648,NULL);
            menu_add_item_menu_valor_opcion(array_menu_machine_selection,8);
            menu_add_item_menu(array_menu_machine_selection,"Timex Computer 2048",MENU_OPCION_NORMAL,hotswap_1648_to_1648,NULL);
            menu_add_item_menu_valor_opcion(array_menu_machine_selection,9);
            menu_add_item_menu(array_menu_machine_selection,"ZX Spectrum+ 128",MENU_OPCION_NORMAL,hotswap_1648_to_128k,NULL);

        }

        //casos maquinas 128k,+2 (y no +2a)
        if (MACHINE_IS_SPECTRUM_128_P2) {
            menu_add_item_menu_inicial(&array_menu_machine_selection,"ZX Spectrum+ 128k",MENU_OPCION_NORMAL,hotswap_128k_to_128k,NULL);
            menu_add_item_menu(array_menu_machine_selection,"ZX Spectrum+ 128k (Spanish)",MENU_OPCION_NORMAL,hotswap_128k_to_128k_spa,NULL);
            menu_add_item_menu(array_menu_machine_selection,"Pentagon",MENU_OPCION_NORMAL,hotswap_128k_to_pentagon,NULL);
            menu_add_item_menu(array_menu_machine_selection,"ZX Spectrum +2",MENU_OPCION_NORMAL,hotswap_128k_to_p2,NULL);
            menu_add_item_menu(array_menu_machine_selection,"ZX Spectrum +2 (French)",MENU_OPCION_NORMAL,hotswap_128k_to_p2f,NULL);
            menu_add_item_menu(array_menu_machine_selection,"ZX Spectrum +2 (Spanish)",MENU_OPCION_NORMAL,hotswap_128k_to_p2_spa,NULL);
            menu_add_item_menu(array_menu_machine_selection,"ZX Spectrum +2A (ROM v4.0)",MENU_OPCION_NORMAL,hotswap_128k_to_p2a,NULL);
            menu_add_item_menu(array_menu_machine_selection,"ZX Spectrum +3 (ROM v4.0)",MENU_OPCION_NORMAL,hotswap_128k_to_p3,NULL);
            menu_add_item_menu(array_menu_machine_selection,"ZX Spectrum 48k",MENU_OPCION_NORMAL,hotswap_128k_to_48k,NULL);
        }

        //maquinas p2a, p3
        if (MACHINE_IS_SPECTRUM_P2A_P3) {
            menu_add_item_menu_inicial(&array_menu_machine_selection,"ZX Spectrum +2A (ROM v4.0)",MENU_OPCION_NORMAL,hotswap_p2a_to_p2a40,NULL);
            menu_add_item_menu(array_menu_machine_selection,"ZX Spectrum +2A (ROM v4.1)",MENU_OPCION_NORMAL,hotswap_p2a_to_p2a41,NULL);
            menu_add_item_menu(array_menu_machine_selection,"ZX Spectrum +2A (Spanish)",MENU_OPCION_NORMAL,hotswap_p2a_to_p2aspa,NULL);
            menu_add_item_menu(array_menu_machine_selection,"ZX Spectrum +3 (ROM v4.0)",MENU_OPCION_NORMAL,hotswap_p2a_to_p3a40,NULL);
            menu_add_item_menu(array_menu_machine_selection,"ZX Spectrum +3 (ROM v4.1)",MENU_OPCION_NORMAL,hotswap_p2a_to_p3a41,NULL);
            menu_add_item_menu(array_menu_machine_selection,"ZX Spectrum +3 (Spanish)",MENU_OPCION_NORMAL,hotswap_p2a_to_p3aspa,NULL);
            menu_add_item_menu(array_menu_machine_selection,"ZX Spectrum 128k",MENU_OPCION_NORMAL,hotswap_p2a_to_128k,NULL);
            menu_add_item_menu(array_menu_machine_selection,"ZX Spectrum 48k",MENU_OPCION_NORMAL,hotswap_p2a_to_48k,NULL);
        }

        //maquinas cpc 4*
        if (MACHINE_IS_CPC_464 || MACHINE_IS_CPC_4128) {
            menu_add_item_menu_inicial(&array_menu_machine_selection,"Amstrad CPC 464",MENU_OPCION_NORMAL,hotswap_cpc_to_464,NULL);
            menu_add_item_menu(array_menu_machine_selection,"Amstrad CPC 4128",MENU_OPCION_NORMAL,hotswap_cpc_to_4128,NULL);
        }

        //maquinas cpc 6*
        if (MACHINE_IS_CPC_6128 || MACHINE_IS_CPC_664) {
            menu_add_item_menu_inicial(&array_menu_machine_selection,"Amstrad CPC 664",MENU_OPCION_NORMAL,hotswap_cpc_to_664,NULL);
            menu_add_item_menu(array_menu_machine_selection,"Amstrad CPC 6128",MENU_OPCION_NORMAL,hotswap_cpc_to_6128,NULL);
        }


        //maquinas pcw
        if (MACHINE_IS_PCW) {
            menu_add_item_menu_inicial(&array_menu_machine_selection,"Amstrad PCW 8256",MENU_OPCION_NORMAL,hotswap_pcw_to_8256,NULL);
            menu_add_item_menu(array_menu_machine_selection,"Amstrad PCW 8512",MENU_OPCION_NORMAL,hotswap_pcw_to_8512,NULL);
        }

        //maquinas zxuno
        if (MACHINE_IS_ZXUNO) {
            menu_add_item_menu_inicial(&array_menu_machine_selection,"ZX Spectrum +2A (ROM v4.0)",MENU_OPCION_NORMAL,hotswap_zxuno_to_p2a,NULL);

            menu_add_item_menu_tooltip(array_menu_machine_selection,"The final machine type is "
            "Spectrum +2A (ROM v4.0) but the data ROM really comes from ZX-Uno");
            menu_add_item_menu_ayuda(array_menu_machine_selection,"The final machine type is "
            "Spectrum +2A (ROM v4.0) but the data ROM really comes from ZX-Uno");


            menu_add_item_menu(array_menu_machine_selection,"ZX Spectrum 48k",MENU_OPCION_NORMAL,hotswap_zxuno_to_48k,NULL);

        }


        //maquinas zx80
        if (MACHINE_IS_ZX80_TYPE) {
            menu_add_item_menu_inicial(&array_menu_machine_selection,"Microdigital TK80",MENU_OPCION_NORMAL,hotswap_zx80_to_zx80,NULL);
			menu_add_item_menu_valor_opcion(array_menu_machine_selection,0);
			menu_add_item_menu(array_menu_machine_selection,"Microdigital TK82",MENU_OPCION_NORMAL,hotswap_zx80_to_zx80,NULL);
			menu_add_item_menu_valor_opcion(array_menu_machine_selection,1);
			menu_add_item_menu(array_menu_machine_selection,"ZX80",MENU_OPCION_NORMAL,hotswap_zx80_to_zx80,NULL);
			menu_add_item_menu_valor_opcion(array_menu_machine_selection,2);
            menu_add_item_menu(array_menu_machine_selection,"ZX81",MENU_OPCION_NORMAL,hotswap_zx80_to_zx81,NULL);
        }

        //maquinas zx81
        if (MACHINE_IS_ZX81_TYPE) {
			menu_add_item_menu_inicial(&array_menu_machine_selection,"Timex Sinclair 1000",MENU_OPCION_NORMAL,hotswap_zx81_to_zx81,NULL);
			menu_add_item_menu_valor_opcion(array_menu_machine_selection,0);
			menu_add_item_menu(array_menu_machine_selection,"Timex Sinclair 1500",MENU_OPCION_NORMAL,hotswap_zx81_to_zx81,NULL);
			menu_add_item_menu_valor_opcion(array_menu_machine_selection,1);
			menu_add_item_menu(array_menu_machine_selection,"Microdigital TK82C",MENU_OPCION_NORMAL,hotswap_zx81_to_zx81,NULL);
			menu_add_item_menu_valor_opcion(array_menu_machine_selection,2);
			menu_add_item_menu(array_menu_machine_selection,"Microdigital TK83",MENU_OPCION_NORMAL,hotswap_zx81_to_zx81,NULL);
			menu_add_item_menu_valor_opcion(array_menu_machine_selection,3);
			menu_add_item_menu(array_menu_machine_selection,"Microdigital TK85",MENU_OPCION_NORMAL,hotswap_zx81_to_zx81,NULL);
			menu_add_item_menu_valor_opcion(array_menu_machine_selection,4);
			menu_add_item_menu(array_menu_machine_selection,"ZX81",MENU_OPCION_NORMAL,hotswap_zx81_to_zx81,NULL);
			menu_add_item_menu_valor_opcion(array_menu_machine_selection,5);
            menu_add_item_menu(array_menu_machine_selection,"ZX80",MENU_OPCION_NORMAL,hotswap_zx81_to_zx80,NULL);
        }


        //maquinas chloe
        if (MACHINE_IS_CHLOE) {
            menu_add_item_menu_inicial(&array_menu_machine_selection,"Chloe 140SE",MENU_OPCION_NORMAL,hotswap_chloe_to_140,NULL);
            menu_add_item_menu(array_menu_machine_selection,"Chloe 280SE",MENU_OPCION_NORMAL,hotswap_chloe_to_280,NULL);
            menu_add_item_menu(array_menu_machine_selection,"ZX Spectrum 48k",MENU_OPCION_NORMAL,hotswap_chloe_to_48k,NULL);


        }

        //Timex 2068
        if (MACHINE_IS_TIMEX_TS_TC_2068) {
            menu_add_item_menu_inicial(&array_menu_machine_selection,"Timex Computer 2068",MENU_OPCION_NORMAL,hotswap_timex2068_to_tc2068,NULL);
			menu_add_item_menu(array_menu_machine_selection,"Timex Sinclair 2068",MENU_OPCION_NORMAL,hotswap_timex2068_to_ts2068,NULL);
			menu_add_item_menu(array_menu_machine_selection,"ZX Spectrum 48k",MENU_OPCION_NORMAL,hotswap_resto_a_48k,NULL);
        }

        //Diferentes maquinas que solo pueden saltar a spectrum 48k
        if (MACHINE_IS_PRISM || MACHINE_IS_TBBLUE || MACHINE_IS_CHROME || MACHINE_IS_ZXEVO) {
            menu_add_item_menu_inicial(&array_menu_machine_selection,"ZX Spectrum 48k",MENU_OPCION_NORMAL,hotswap_resto_a_48k,NULL);
        }




        menu_add_item_menu(array_menu_machine_selection,"",MENU_OPCION_SEPARADOR,NULL,NULL);
        //menu_add_item_menu(array_menu_machine_selection,"ESC Back",MENU_OPCION_NORMAL|MENU_OPCION_ESC,NULL,NULL);
        menu_add_ESC_item(array_menu_machine_selection);

        retorno_menu=menu_dibuja_menu_no_title_lang(&hotswap_machine_opcion_seleccionada,&item_seleccionado,array_menu_machine_selection,"Hotswap Machine Menu" );


        if ((item_seleccionado.tipo_opcion&MENU_OPCION_ESC)==0 && retorno_menu>=0) {
            //llamamos por valor de funcion
            if (item_seleccionado.menu_funcion!=NULL) {
                //printf ("actuamos por funcion\n");
                item_seleccionado.menu_funcion(item_seleccionado.valor_opcion);

                //De este menu salir siempre
                salir_todos_menus=1;

            }
        }


    } while ( (item_seleccionado.tipo_opcion&MENU_OPCION_ESC)==0 && retorno_menu!=MENU_RETORNO_ESC && !salir_todos_menus);
}




//Si se acaba de tocar parametros de custom rom, habilitar una opcion para hacer reset a esa maquina
int menu_custom_rom_changed=0;


void menu_custom_machine_romfile(MENU_ITEM_PARAMETERS)
{

        char *filtros[2];

        filtros[0]="rom";
        filtros[1]=0;


        if (menu_filesel("Select ROM File",filtros,custom_romfile)==1) {
                //

        }

        else {
                custom_romfile[0]=0;
        }

        menu_custom_rom_changed=1;
}





int menu_hotswap_machine_cond(void) {

	//Retornar ok solo para determinadas maquinas
	if (MACHINE_IS_SPECTRUM_16_48)  return 1;
	if (MACHINE_IS_SPECTRUM_128_P2)  return 1;
	if (MACHINE_IS_SPECTRUM_P2A_P3)  return 1;
    if (MACHINE_IS_ZXUNO)  return 1;
	if (MACHINE_IS_ZX8081)  return 1;
	if (MACHINE_IS_CHLOE)  return 1;
	if (MACHINE_IS_PRISM)  return 1;
	if (MACHINE_IS_TIMEX_TS_TC_2068)  return 1;
	if (MACHINE_IS_TBBLUE)  return 1;
	if (MACHINE_IS_CHROME)  return 1;
	if (MACHINE_IS_ZXEVO)  return 1;
	if (MACHINE_IS_CPC)  return 1;
    if (MACHINE_IS_PCW) return 1;


	return 0;
}



void menu_machine_set_machine_by_id(int id_maquina)
{
    current_machine_type=id_maquina;

    /*if (!setting_set_machine_enable_custom_rom) {
        set_machine(NULL);
    }
    else {
        set_machine(custom_romfile);
    }*/

    //en rom_load ya tiene en cuenta si se usa setting_set_machine_enable_custom_rom
    set_machine(NULL);

    cold_start_cpu_registers();
    reset_cpu();

    //desactivar autoload
    //noautoload.v=1;
    //initial_tap_load.v=0;


    //expulsamos cintas
    eject_tape_load();
    eject_tape_save();

    //Y salimos de todos los menus
    salir_todos_menus=1;



    if (MACHINE_IS_TBBLUE) {
        //Si se pregunta si se quiere autoconfigurar SD, solo si esta el grabado de configuracion, e interfaz permite menu (no stdout ni simpletext ni null)
        if (save_configuration_file_on_exit.v && tbblue_autoconfigure_sd_asked.v==0 && si_normal_menu_video_driver()) {
            if (menu_confirm_yesno_texto("Autoconfigure Initial SD","Sure?")) {
                menu_storage_mmc_autoconfigure_tbblue(0);
            }

            tbblue_autoconfigure_sd_asked.v=1;
        }

    }

    if (MACHINE_IS_SG1000) {
        menu_first_aid("sg1000_boot");
    }

}




void menu_machine_selection_manufacturer_machines(int fabricante)
{


	char *nombre_maquina;

	int total_maquinas=0;

    //por defecto
    machine_selection_por_fabricante_opcion_seleccionada=0;

    //Seleccion por fabricante
    menu_item *array_menu_machine_selection_por_fabricante;
    menu_item item_seleccionado;
    int retorno_menu;
	do {

        menu_add_item_menu_inicial(&array_menu_machine_selection_por_fabricante,"",MENU_OPCION_UNASSIGNED,NULL,NULL);


        int i=0;

        while (machines_info[i].family_id!=MACHINE_FAMILY_EOF) {
            if (machines_info[i].fabricante==fabricante) {
                nombre_maquina=machines_info[i].nombre_maquina;
                menu_add_item_menu_format(array_menu_machine_selection_por_fabricante,MENU_OPCION_NORMAL,NULL,NULL,"%s",nombre_maquina);
                menu_add_item_menu_valor_opcion(array_menu_machine_selection_por_fabricante,machines_info[i].machine_id);
                menu_add_item_menu_es_sencillo(array_menu_machine_selection_por_fabricante);

                //cambiar linea seleccionada a maquina en cuestion
                //siempre que no estemos en un full index rescan del buscador de opciones
                if (!menu_dibuja_menu_recorrer_menus) {
                    if (machines_info[i].machine_id==current_machine_type) {
                        //printf("coincide maquina en %d\n",total_maquinas);
                        machine_selection_por_fabricante_opcion_seleccionada=total_maquinas;
                    }
                }

                total_maquinas++;

            }
            i++;
        }


        menu_add_item_menu(array_menu_machine_selection_por_fabricante,"",MENU_OPCION_SEPARADOR,NULL,NULL);


        //menu_add_item_menu(array_menu_machine_selection_por_fabricante,"ESC Back",MENU_OPCION_NORMAL|MENU_OPCION_ESC,NULL,NULL);
        menu_add_ESC_item(array_menu_machine_selection_por_fabricante);

        //char titulo_con_hotkey[100];
        char titulo[100];
        //sprintf(titulo,"%s",array_fabricantes_hotkey[fabricante]);
        menu_convierte_texto_sin_modificadores(array_fabricantes_hotkey[fabricante],titulo);

        //retorno_menu=menu_dibuja_menu_no_title_lang(&machine_selection_por_fabricante_opcion_seleccionada,&item_seleccionado,array_menu_machine_selection_por_fabricante,"Select machine" );
        retorno_menu=menu_dibuja_menu_no_title_lang(&machine_selection_por_fabricante_opcion_seleccionada,&item_seleccionado,array_menu_machine_selection_por_fabricante,titulo );

        //printf ("Opcion seleccionada: %d\n",machine_selection_por_fabricante_opcion_seleccionada);



        if ((item_seleccionado.tipo_opcion&MENU_OPCION_ESC)==0 && retorno_menu>=0) {

            if (machine_selection_por_fabricante_opcion_seleccionada>=0 && machine_selection_por_fabricante_opcion_seleccionada<=total_maquinas) {

                //printf ("Seleccion opcion=%d\n",machine_selection_por_fabricante_opcion_seleccionada);
                int id_maquina=item_seleccionado.valor_opcion;

                menu_machine_set_machine_by_id(id_maquina);

            }



            //llamamos por valor de funcion
            if (item_seleccionado.menu_funcion!=NULL) {
                    //printf ("actuamos por funcion\n");
                    item_seleccionado.menu_funcion(item_seleccionado.valor_opcion);
            }



        }


    } while ( (item_seleccionado.tipo_opcion&MENU_OPCION_ESC)==0 && retorno_menu!=MENU_RETORNO_ESC && !salir_todos_menus);


}

void menu_custom_machine_toggle(MENU_ITEM_PARAMETERS)
{
    setting_set_machine_enable_custom_rom ^=1;
    menu_custom_rom_changed=1;
}

void menu_custom_machine_set_p2e(MENU_ITEM_PARAMETERS)
{
    int existe=find_sharedfile(CUSTOM_MACHINE_2E_MMC_ROM_FILE,custom_romfile);

    if (!existe)  {
        debug_printf(VERBOSE_ERR,"Unable to find %s",CUSTOM_MACHINE_2E_MMC_ROM_FILE);
        return;
    }

    menu_custom_rom_changed=1;
}




void menu_custom_machine_reset(MENU_ITEM_PARAMETERS)
{
    menu_custom_rom_changed=0;
    menu_machine_set_machine_by_id(current_machine_type);
}

//Indicar que hay que volver a cargar el menu de Machines selection porque se ha cambiado el tipo de lista
int menu_machine_selection_cambio_tipo_lista=0;
//Indicar que se ha cambiado el tipo de lista y mover el item seleccionado a la opcion de tipo lista, para que el usuario no
//pierda el cursor
int menu_machine_selection_cambio_tipo_lista_conservar_cursor=0;

void menu_setting_select_machine_by_name(MENU_ITEM_PARAMETERS)
{
	setting_machine_selection_type++;
    if (setting_machine_selection_type>MACHINE_SELECTION_TYPE_BY_FAMILY) {
        setting_machine_selection_type=MACHINE_SELECTION_TYPE_BY_MANUFACTURER;
    }
    menu_machine_selection_cambio_tipo_lista=1;
    menu_machine_selection_cambio_tipo_lista_conservar_cursor=1;
}


//Agregar items comunes a los dos menus de maquina: por fabricante o por nombre de maquina
//Se le pasa la variable de opcion seleccionada por si se necesita alterar
void menu_machine_selection_common_items(menu_item *m,int *opcion_seleccionada)
{

        menu_add_item_menu(m,"",MENU_OPCION_SEPARADOR,NULL,NULL);
        menu_add_item_menu_es_avanzado(m);

        char machine_selection_type[20];

        if (setting_machine_selection_type==MACHINE_SELECTION_TYPE_BY_NAME) {
            strcpy(machine_selection_type,"Name");
        }
        else if (setting_machine_selection_type==MACHINE_SELECTION_TYPE_BY_FAMILY) {
            strcpy(machine_selection_type,"Family");
        }
        else {
            strcpy(machine_selection_type,"Manufacturer");
        }

		menu_add_item_menu_en_es_ca(m,MENU_OPCION_NORMAL,menu_setting_select_machine_by_name,NULL,
            "List type","Tipo listado","Tipus llistat");
		menu_add_item_menu_prefijo_format(m,"    ");
        menu_add_item_menu_sufijo_format(m," [%s]",machine_selection_type);
		menu_add_item_menu_tooltip(m,"Select machine by manufacturer, name or family");
		menu_add_item_menu_ayuda(m,"Select machine by manufacturer, name or family");
        menu_add_item_menu_es_avanzado(m);

        if (menu_machine_selection_cambio_tipo_lista_conservar_cursor) {
            menu_machine_selection_cambio_tipo_lista_conservar_cursor=0;
            int linea_actual=menu_item_get_linea_actual(m);
            *opcion_seleccionada=linea_actual;
        }

        menu_add_item_menu_en_es_ca(m,MENU_OPCION_NORMAL,menu_custom_machine_toggle,NULL,
            "Custom rom","Rom personalizada","Rom personalitzada");
        menu_add_item_menu_prefijo_format(m,"[%c] ",(setting_set_machine_enable_custom_rom ? 'X' : ' ' ));
        menu_add_item_menu_tooltip(m,"Select a custom rom for any machine you select");
        menu_add_item_menu_ayuda(m,"Select a custom rom for any machine you select");
        menu_add_item_menu_es_avanzado(m);

        if (setting_set_machine_enable_custom_rom) {
            char string_romfile_shown[20];
            menu_tape_settings_trunc_name(custom_romfile,string_romfile_shown,20);

            menu_add_item_menu_en_es_ca(m,MENU_OPCION_NORMAL,menu_custom_machine_romfile,NULL,
                " Rom file"," Archivo rom"," Arxiu rom");
            menu_add_item_menu_sufijo_format(m,": %s",string_romfile_shown);
            menu_add_item_menu_es_avanzado(m);

            if (MACHINE_IS_SPECTRUM_P2A_P3) {
                menu_add_item_menu_en_es_ca(m,MENU_OPCION_NORMAL,menu_custom_machine_set_p2e,NULL,
                    " Set +2e/+3e rom"," Establecer rom +2e/+3e"," Establir rom +2e/+3e");
                menu_add_item_menu_es_avanzado(m);
            }
        }

        if (menu_custom_rom_changed) {
            menu_add_item_menu_en_es_ca(m,MENU_OPCION_NORMAL,menu_custom_machine_reset,NULL,
            " Reset machine"," Reset máquina"," Reset màquina");
            menu_add_item_menu_es_avanzado(m);
        }

        //Solo separar en modo avanzado, para las opciones de hotswap y custom machine
        menu_add_item_menu(m,"",MENU_OPCION_SEPARADOR,NULL,NULL);
        menu_add_item_menu_es_avanzado(m);

        //Hotswap de Z88 o Jupiter Ace o CHLOE no existe
        menu_add_item_menu(m,"~~Hotswap machine",MENU_OPCION_NORMAL,menu_hotswap_machine,menu_hotswap_machine_cond);
        menu_add_item_menu_shortcut(m,'h');
        menu_add_item_menu_tooltip(m,"Change machine type without resetting");
        menu_add_item_menu_ayuda(m,"Change machine type without resetting.");
        menu_add_item_menu_tiene_submenu(m);
        menu_add_item_menu_es_avanzado(m);




        menu_add_item_menu(m,"",MENU_OPCION_SEPARADOR,NULL,NULL);

}

//Seleccion de maquina por fabricante
void menu_machine_selection_manufacturer(MENU_ITEM_PARAMETERS)
{

    //Seleccion por fabricante
    menu_item *array_menu_machine_selection;
    menu_item item_seleccionado;
    int retorno_menu;

    //return_fabricante_maquina
    //Establecemos linea menu segun fabricante activo
    //Siempre que no estemos en un full index rescan del buscador de opciones
    if (!menu_dibuja_menu_recorrer_menus) {
        machine_selection_opcion_seleccionada=return_fabricante_maquina(current_machine_type)+1;
    }

    do {

        //menu_add_item_menu_inicial(&array_menu_machine_selection,"",MENU_OPCION_UNASSIGNED,NULL,NULL);
        menu_add_item_menu_en_es_ca_inicial(&array_menu_machine_selection,MENU_OPCION_SEPARADOR,NULL,NULL,
        "--Select manufacturer--","--Selecciona fabricante--","--Selecciona fabricant--");
        menu_add_item_menu_es_sencillo(array_menu_machine_selection);

        //Primer fabricante
        //menu_add_item_menu_inicial_format(&array_menu_machine_selection,MENU_OPCION_NORMAL,NULL,NULL,"%s",array_fabricantes_hotkey[0]);
        //menu_add_item_menu_shortcut(array_menu_machine_selection,array_fabricantes_hotkey_letra[0]);

		//Fabricantes
        int i;
        for (i=0;i<TOTAL_FABRICANTES;i++) {
            menu_add_item_menu_format(array_menu_machine_selection,MENU_OPCION_NORMAL,NULL,NULL,"%s",array_fabricantes_hotkey[i]);
            z80_byte letra=array_fabricantes_hotkey_letra[i];
            if (letra!=' ') menu_add_item_menu_shortcut(array_menu_machine_selection,letra);
            menu_add_item_menu_tiene_submenu(array_menu_machine_selection);
            menu_add_item_menu_es_sencillo(array_menu_machine_selection);
        }

        menu_machine_selection_common_items(array_menu_machine_selection,&machine_selection_opcion_seleccionada);


        menu_add_ESC_item(array_menu_machine_selection);

        //La llamada a menu_add_item_menu_index_full_path lo agrega al principio del array, por tanto lo podemos llamar desde cualquier
        //punto de la creacion del menu. Lo llamo desde aqui porque queda esteticamente mas bonito
        menu_add_item_menu_index_full_path(array_menu_machine_selection,"Main Menu-> Machine","Menú Principal-> Máquina","Menú Principal-> Màquina");

        retorno_menu=menu_dibuja_menu(&machine_selection_opcion_seleccionada,&item_seleccionado,array_menu_machine_selection,
            "Machine menu","Menú Máquina","Menú Màquina" );


        if ((item_seleccionado.tipo_opcion&MENU_OPCION_ESC)==0 && retorno_menu>=0) {

            if (machine_selection_opcion_seleccionada>=0 && machine_selection_opcion_seleccionada<=TOTAL_FABRICANTES) {


                menu_machine_selection_manufacturer_machines(machine_selection_opcion_seleccionada-1);


            }
            //llamamos por valor de funcion
            if (item_seleccionado.menu_funcion!=NULL) {
                //printf ("actuamos por funcion\n");
                item_seleccionado.menu_funcion(item_seleccionado.valor_opcion);

            }

        }

    } while ( (item_seleccionado.tipo_opcion&MENU_OPCION_ESC)==0 && retorno_menu!=MENU_RETORNO_ESC && !salir_todos_menus && !menu_machine_selection_cambio_tipo_lista);


}



void menu_machine_selection_family_machines(unsigned int id_familia)
{

    menu_item *array_menu_common;
    menu_item item_seleccionado;
    int retorno_menu;

    int total_maquinas=0;

    int opcion_seleccionada=0;

	do {

        menu_add_item_menu_inicial(&array_menu_common,"",MENU_OPCION_UNASSIGNED,NULL,NULL);

        //Recorrer todas las máquinas y sacar las que sean de familia id_familia

        int i;

        for (i=0;i<99999 && machines_info[i].nombre_maquina[0]!=0;i++) {
            //printf ("id: %03d nombre: %s\n",machines_info[i].id,machines_info[i].nombre_maquina);
            int id_maquina=machines_info[i].machine_id;
            enum machine_families_list familia_maquina=debug_machine_get_id_family(id_maquina);
            if (familia_maquina==id_familia) {
                menu_add_item_menu_format(array_menu_common,MENU_OPCION_NORMAL,NULL,NULL,"%s",machines_info[i].nombre_maquina);
                menu_add_item_menu_valor_opcion(array_menu_common,machines_info[i].machine_id);
                menu_add_item_menu_es_sencillo(array_menu_common);

                //Mover el cursor a la máquina seleccionada
                //Siempre que no estemos en un full index rescan del buscador de opciones
                if (!menu_dibuja_menu_recorrer_menus) {
                    if (id_maquina==current_machine_type) opcion_seleccionada=total_maquinas;
                }

                total_maquinas++;

            }

        }

        menu_add_item_menu_separator(array_menu_common);

        menu_add_ESC_item(array_menu_common);


        char titulo[100];
        sprintf(titulo,"%s",debug_machine_info_family_get_family(id_familia) );

        retorno_menu=menu_dibuja_menu_no_title_lang(&opcion_seleccionada,&item_seleccionado,array_menu_common,titulo );


        if ((item_seleccionado.tipo_opcion&MENU_OPCION_ESC)==0 && retorno_menu>=0) {

            if (opcion_seleccionada>=0 && opcion_seleccionada<total_maquinas) {

                int id_maquina=item_seleccionado.valor_opcion;
                menu_machine_set_machine_by_id(id_maquina);

            }


            //llamamos por valor de funcion
            if (item_seleccionado.menu_funcion!=NULL) {
                //printf ("actuamos por funcion\n");
                item_seleccionado.menu_funcion(item_seleccionado.valor_opcion);
            }


        }

    } while ( (item_seleccionado.tipo_opcion&MENU_OPCION_ESC)==0 && retorno_menu!=MENU_RETORNO_ESC && !salir_todos_menus);


}

//Seleccion de maquina por familia
void menu_machine_selection_by_family(MENU_ITEM_PARAMETERS)
{

    menu_item *array_menu_machine_selection;
    menu_item item_seleccionado;
    int retorno_menu;

    int opcion_seleccionada=1;

    int modificada_linea_familia=0;

    //Para poder marcar el cursor en la familia de la maquina actual
    enum machine_families_list familia_maquina_actual=debug_machine_get_id_family(current_machine_type);

    do {

        menu_add_item_menu_en_es_ca_inicial(&array_menu_machine_selection,MENU_OPCION_SEPARADOR,NULL,NULL,
            "--Select family--","--Selecciona familia--","--Selecciona familia--");
        menu_add_item_menu_es_sencillo(array_menu_machine_selection);


        int i=0;
        int total_families=0;

        while (family_names[i].family_id!=MACHINE_FAMILY_EOF) {

            //Mover el cursor a la familia seleccionada, pero solo la primera vez al entrar en este menu,
            //para evitar que cuando se vaya a submenu y vuelva aqui, no se altere
            //Siempre que no estemos en un full index rescan del buscador de opciones
            if (!menu_dibuja_menu_recorrer_menus && !modificada_linea_familia) {
                if (familia_maquina_actual==family_names[i].family_id) {
                    opcion_seleccionada=i+1;
                    modificada_linea_familia=1;
                }
            }

            menu_add_item_menu_format(array_menu_machine_selection,MENU_OPCION_NORMAL,NULL,NULL,"%s",family_names[i].family_name_con_hotkey);
            if (family_names[i].hotkey) menu_add_item_menu_shortcut(array_menu_machine_selection,family_names[i].hotkey);
            menu_add_item_menu_tiene_submenu(array_menu_machine_selection);
            menu_add_item_menu_es_sencillo(array_menu_machine_selection);
            i++;
            total_families++;
        }

        menu_machine_selection_common_items(array_menu_machine_selection,&opcion_seleccionada);


        menu_add_ESC_item(array_menu_machine_selection);

        //La llamada a menu_add_item_menu_index_full_path lo agrega al principio del array, por tanto lo podemos llamar desde cualquier
        //punto de la creacion del menu. Lo llamo desde aqui porque queda esteticamente mas bonito
        menu_add_item_menu_index_full_path(array_menu_machine_selection,"Main Menu-> Machine","Menú Principal-> Máquina","Menú Principal-> Màquina");

        retorno_menu=menu_dibuja_menu(&opcion_seleccionada,&item_seleccionado,array_menu_machine_selection,
            "Machine menu","Menú Máquina","Menú Màquina" );


        if ((item_seleccionado.tipo_opcion&MENU_OPCION_ESC)==0 && retorno_menu>=0) {

            if (opcion_seleccionada>=1 && opcion_seleccionada<=total_families) {
                menu_machine_selection_family_machines(opcion_seleccionada-1);
            }

            //llamamos por valor de funcion
            if (item_seleccionado.menu_funcion!=NULL) {
                //printf ("actuamos por funcion\n");
                item_seleccionado.menu_funcion(item_seleccionado.valor_opcion);

            }

        }

    } while ( (item_seleccionado.tipo_opcion&MENU_OPCION_ESC)==0 && retorno_menu!=MENU_RETORNO_ESC && !salir_todos_menus && !menu_machine_selection_cambio_tipo_lista);


}


int menu_machine_selection_by_name_alphasort(const struct s_machines_info **d1, const struct s_machines_info **d2)
{

        //printf ("menu_filesel_alphasort %s %s\n",(*d1)->d_name,(*d2)->d_name );

        //compara nombre
        return (strcasecmp((*d1)->nombre_maquina,(*d2)->nombre_maquina));
}

void menu_machine_selection_by_name_set(MENU_ITEM_PARAMETERS)
{
	menu_machine_set_machine_by_id(valor_opcion);
}

//Seleccion de maquina por nombre
void menu_machine_selection_by_name(MENU_ITEM_PARAMETERS)
{


	int total_maquinas;


	total_maquinas=0;

	int i;

	for (i=0;i<99999 && machines_info[i].nombre_maquina[0]!=0;i++) {
		//printf ("id: %03d nombre: %s\n",machines_info[i].id,machines_info[i].nombre_maquina);
		total_maquinas++;
	}

	//printf ("total maquinas: %d\n",total_maquinas);


	//Meterlas en array, para poderlas ordenar

	struct s_machines_info *sorted_machines_info;

	int tamanyo_struct=sizeof(struct s_machines_info);

	int tamanyo_total=tamanyo_struct*total_maquinas;

	//printf ("Allocating memory for %d\n",tamanyo_total);

	sorted_machines_info=malloc(tamanyo_total);

	if (sorted_machines_info==NULL) cpu_panic ("Cannot allocate memory for machine list");

	//Insertar listado en memoria
	for (i=0;i<total_maquinas;i++) {
		//printf ("id: %03d nombre: %s\n",machines_info[i].id,machines_info[i].nombre_maquina);

		strcpy(sorted_machines_info[i].nombre_maquina,machines_info[i].nombre_maquina);
		sorted_machines_info[i].machine_id=machines_info[i].machine_id;
	}

	//Array de punteros para poderlos ordenar
	struct s_machines_info **memoria_punteros;


	//Asignamos memoria
    memoria_punteros=malloc(sizeof(struct dirent *)*total_maquinas);


    if (memoria_punteros==NULL) {
                cpu_panic("Error allocating memory when sorting machine list");
    }

	//Meter en esa lista de punteros cada item de la estructura
	for (i=0;i<total_maquinas;i++) {
		memoria_punteros[i]=&sorted_machines_info[i];
	}

	//Ordenar ese listado de punteros

	//lanzar qsort
	int (*funcion_compar)(const void *, const void *);

	funcion_compar=( int (*)(const void *, const void *)  ) menu_machine_selection_by_name_alphasort;

	qsort(memoria_punteros,total_maquinas,sizeof(struct s_machines_info *), funcion_compar);

	//Imprimir listado de memoria
	/*for (i=0;i<total_maquinas;i++) {
		printf ("sorted id: %03d nombre: %s\n",memoria_punteros[i]->id,memoria_punteros[i]->nombre_maquina);

	}
	*/


	//Meter cada uno en menu. Asignar id maquina como valor_opcion

	//Dado que es una variable local, siempre podemos usar este nombre array_menu_common
	menu_item *array_menu_common;
	menu_item item_seleccionado;
	int retorno_menu;


	do {


		//Como no sabemos cual sera el item inicial, metemos este sin asignar, que se sobreescribe en el siguiente menu_add_item_menu
		//menu_add_item_menu_inicial(&array_menu_common,"",MENU_OPCION_UNASSIGNED,NULL,NULL);

        menu_add_item_menu_en_es_ca_inicial(&array_menu_common,MENU_OPCION_SEPARADOR,NULL,NULL,
            "--Select machine--","--Selecciona máquina--","--Selecciona màquina--");
        menu_add_item_menu_es_sencillo(array_menu_common);

		for (i=0;i<total_maquinas;i++) {
			//printf ("sorted id: %03d nombre: %s\n",memoria_punteros[i]->id,memoria_punteros[i]->nombre_maquina);
			menu_add_item_menu_format(array_menu_common,MENU_OPCION_NORMAL,menu_machine_selection_by_name_set,NULL,memoria_punteros[i]->nombre_maquina);
            menu_add_item_menu_es_sencillo(array_menu_common);

			menu_add_item_menu_valor_opcion(array_menu_common,memoria_punteros[i]->machine_id);

			//Si coincide id con la maquina actual, cambiar menu_machine_selection_by_name_opcion_seleccionada
            //Siempre que no estemos en un full index rescan del buscador de opciones
			if (current_machine_type==memoria_punteros[i]->machine_id) {
                if (!menu_dibuja_menu_recorrer_menus) {
				    menu_machine_selection_by_name_opcion_seleccionada=i+1;
                }
				//printf ("Match current machine %d with list entry %d. Changing last selected\n",current_machine_type,i);
			}

		}

        menu_machine_selection_common_items(array_menu_common,&menu_machine_selection_by_name_opcion_seleccionada);


		menu_add_ESC_item(array_menu_common);

        //La llamada a menu_add_item_menu_index_full_path lo agrega al principio del array, por tanto lo podemos llamar desde cualquier
        //punto de la creacion del menu. Lo llamo desde aqui porque queda esteticamente mas bonito
        menu_add_item_menu_index_full_path(array_menu_common,"Main Menu-> Machine","Menú Principal-> Máquina","Menú Principal-> Màquina");

		retorno_menu=menu_dibuja_menu(&menu_machine_selection_by_name_opcion_seleccionada,&item_seleccionado,array_menu_common,
            "Machine menu","Menú Máquina","Menú Màquina" );


			if ((item_seleccionado.tipo_opcion&MENU_OPCION_ESC)==0 && retorno_menu>=0) {
					//llamamos por valor de funcion
					if (item_seleccionado.menu_funcion!=NULL) {
							//printf ("actuamos por funcion\n");
							item_seleccionado.menu_funcion(item_seleccionado.valor_opcion);

					}
			}

	} while ( (item_seleccionado.tipo_opcion&MENU_OPCION_ESC)==0 && retorno_menu!=MENU_RETORNO_ESC && !salir_todos_menus && !menu_machine_selection_cambio_tipo_lista);


	free(sorted_machines_info);
	free(memoria_punteros);




}

void menu_machine_selection(MENU_ITEM_PARAMETERS)
{

    //Si somos cliente de partida online como slave, no permitir esto
    if (zeng_online_connected.v && zeng_online_i_am_master.v==0) {
        menu_error_message("You can't change machine while connected to ZENG online");
        //Evitar que se reabra menu principal, que genera duplicado el mismo menu,
        //dado que no se ha salido del menu con tecla cursor izquierda
        //Asi cerramos todos los menus
        salir_todos_menus=1;
        return;
    }

    menu_custom_rom_changed=0;
    do {
        menu_machine_selection_cambio_tipo_lista=0;

        switch (setting_machine_selection_type) {
            case MACHINE_SELECTION_TYPE_BY_MANUFACTURER:
                menu_machine_selection_manufacturer(0);
            break;

            case MACHINE_SELECTION_TYPE_BY_NAME:
                menu_machine_selection_by_name(0);
            break;

            case MACHINE_SELECTION_TYPE_BY_FAMILY:
                menu_machine_selection_by_family(0);
            break;

            default:
                //no se deberia llegar nunca aqui
                menu_machine_selection_manufacturer(0);
            break;
        }

        //Recargar si se ha modificado tipo lista
    } while (menu_machine_selection_cambio_tipo_lista);
}

//Retorna puntero a memoria con contenido de archivo leido. NULL si ha habido algun error
char *menu_about_read_file_only_read(char *aboutfile,int show_err_if_big)
{

    char *buffer_memory=util_malloc(MAX_TEXTO_GENERIC_MESSAGE,"Can not allocate memory for reading the file");

	debug_printf (VERBOSE_INFO,"Loading %s File",aboutfile);
	FILE *ptr_aboutfile;
	//ptr_aboutfile=fopen(aboutfile,"rb");
	open_sharedfile(aboutfile,&ptr_aboutfile);

	if (!ptr_aboutfile)
	{
		debug_printf (VERBOSE_ERR,"Unable to open %s file",aboutfile);
        free(buffer_memory);
        return NULL;
	}
	else {

		int leidos=fread(buffer_memory,1,MAX_TEXTO_GENERIC_MESSAGE,ptr_aboutfile);
		debug_printf (VERBOSE_INFO,"Read %d bytes of file: %s",leidos,aboutfile);

        int message_verbose=VERBOSE_DEBUG;

		if (leidos==MAX_TEXTO_GENERIC_MESSAGE) {
            if (show_err_if_big) {
			    message_verbose=VERBOSE_ERR;
            }

            debug_printf (message_verbose,"Reached maximum text buffer: %d bytes. Showing only the first %d bytes",leidos,leidos);
			leidos--;
		}

		buffer_memory[leidos]=0;


		fclose(ptr_aboutfile);


	}

    return buffer_memory;


}



void menu_about_read_file(char *title,char *aboutfile,int show_err_if_big)
{

    char *buffer_memory=menu_about_read_file_only_read(aboutfile,show_err_if_big);

    if (buffer_memory==NULL) return;

    menu_generic_message(title,buffer_memory);

    free(buffer_memory);


}

//Comun para el menu help-changelog y para cuando se muestra el changelog al actualizar
void menu_about_changelog_common(int show_err_if_big)
{
    if (gui_language==GUI_LANGUAGE_SPANISH) {
        menu_about_read_file("Cambios","Cambios",show_err_if_big);
    }
    else menu_about_read_file("Changelog","Changelog",show_err_if_big);
}

void menu_about_changelog(MENU_ITEM_PARAMETERS)
{
    menu_about_changelog_common(1);
}


void menu_about_history(MENU_ITEM_PARAMETERS)
{
	menu_about_read_file("History","HISTORY",1);
}

void menu_about_features(MENU_ITEM_PARAMETERS)
{
    if (gui_language==GUI_LANGUAGE_SPANISH) {
        menu_about_read_file("Caracteristicas","FEATURES_es",1);
    }
    else menu_about_read_file("Features","FEATURES",1);
}

void menu_about_exclusivefeatures(MENU_ITEM_PARAMETERS)
{
        menu_about_read_file("Exclusive Features","EXCLUSIVEFEATURES",1);
}

void menu_about_readme(MENU_ITEM_PARAMETERS)
{
        menu_about_read_file("Readme","README",1);
}


void menu_about_install(MENU_ITEM_PARAMETERS)
{
        menu_about_read_file("Install","INSTALL",1);
}

void menu_about_installwindows(MENU_ITEM_PARAMETERS)
{
        menu_about_read_file("Install on Windows","INSTALLWINDOWS",1);
}

void menu_about_alternate_roms(MENU_ITEM_PARAMETERS)
{
        menu_about_read_file("Alternate ROMS","ALTERNATEROMS",1);
}

void menu_about_included_tapes(MENU_ITEM_PARAMETERS)
{
        menu_about_read_file("Included Tapes","INCLUDEDTAPES",1);
}



void menu_about_acknowledgements(MENU_ITEM_PARAMETERS)
{
        menu_about_read_file("Acknowledgements","ACKNOWLEDGEMENTS",1);
}

void menu_about_donate(MENU_ITEM_PARAMETERS)
{
    char *buffer_memory=menu_about_read_file_only_read("DONATE",1);

    if (buffer_memory==NULL) return;

    char *string_to_add="\n"
                        "To donate, go to the ZEsarUX website (https://github.com/chernandezba/zesarux) "
                        "and look for the DONATE section"
                        "\n"
                        "\n"
                        "Thanks!";

    if (util_concat_string(buffer_memory,string_to_add,MAX_TEXTO_GENERIC_MESSAGE)) {
        debug_printf(VERBOSE_ERR,"Error appending text");
    }

    else {
        menu_generic_message("Donate",buffer_memory);
    }


    free(buffer_memory);

}

void menu_about_donors(MENU_ITEM_PARAMETERS)
{
        menu_about_read_file("Donors","DONORS",1);
}

void menu_about_faq(MENU_ITEM_PARAMETERS)
{
        menu_about_read_file("FAQ","FAQ",1);
}

void menu_about_license(MENU_ITEM_PARAMETERS)
{
        menu_about_read_file("ZEsarUX License","LICENSE",1);
}

void menu_about_licenses_info(MENU_ITEM_PARAMETERS)
{
        menu_about_read_file("Licenses information","LICENSES_info",1);
}

void menu_about_license_playtzx(MENU_ITEM_PARAMETERS)
{
        menu_about_read_file("PlayTZX","licenses/LICENSE_playtzx",1);
}

void menu_about_license_motorola_core(MENU_ITEM_PARAMETERS)
{
        menu_about_read_file("Motorola Core License","licenses/LICENSE_MOTOROLA_CORE",1);
}

void menu_about_license_scmp_core(MENU_ITEM_PARAMETERS)
{
        menu_about_read_file("SCMP Core License","licenses/LICENSE_SCMP_CORE",1);
}

void menu_about_license_scl2trd(MENU_ITEM_PARAMETERS)
{
        menu_about_read_file("scl2trd License","licenses/LICENSE_scl2trd",1);
}

void menu_about_license_fuse(MENU_ITEM_PARAMETERS)
{
        menu_about_read_file("Fuse License","licenses/LICENSE_fuse",1);
}

void menu_about_license_atomlite(MENU_ITEM_PARAMETERS)
{
        menu_about_read_file("SimCoupe License","licenses/LICENSE_simcoupe",1);
}

void menu_about_license_dosfont(MENU_ITEM_PARAMETERS)
{
        menu_about_read_file("DOS font License","licenses/LICENSE_DOS_font",1);
}

void menu_about_license_unrealspeccy(MENU_ITEM_PARAMETERS)
{
        menu_about_read_file("UnrealSpeccy License","licenses/LICENSE_unrealspeccy",1);
}

void menu_about_license_mdvtool(MENU_ITEM_PARAMETERS)
{
        menu_about_read_file("mdvtool License","licenses/LICENSE_mdvtool",1);
}

void menu_about_license_zip(MENU_ITEM_PARAMETERS)
{
        menu_about_read_file("zip License","licenses/LICENSE_zip",1);
}

void menu_about_license_unpaws(MENU_ITEM_PARAMETERS)
{
        menu_about_read_file("unpaws/unquill License","licenses/LICENSE_unpaws",1);
}

void menu_about_license_undaad(MENU_ITEM_PARAMETERS)
{
        menu_about_read_file("undaad License","licenses/LICENSE_undaad",1);
}

void menu_about_license_grackle(MENU_ITEM_PARAMETERS)
{
        menu_about_read_file("grackle License","licenses/LICENSE_grackle",1);
}

void menu_about_license_ungac(MENU_ITEM_PARAMETERS)
{
        menu_about_read_file("ungac License","licenses/LICENSE_ungac",1);
}

void menu_about_license_qemu(MENU_ITEM_PARAMETERS)
{
        menu_about_read_file("Qemu License","licenses/LICENSE_qemu",1);
}

void menu_about_license_hilow(MENU_ITEM_PARAMETERS)
{
        menu_about_read_file("HiLow License","licenses/LICENSE_hilow",1);
}

void menu_about_license_fatfs(MENU_ITEM_PARAMETERS)
{
        menu_about_read_file("FatFs License","licenses/LICENSE_FatFs",1);
}


void menu_about_statistics(MENU_ITEM_PARAMETERS)
{

 int tiempo_trabajado_en_zesarux=timer_get_worked_time();

	menu_generic_message_format("Statistics",
		"Source code lines: %d\n"
        "Code comments: %d\n"
        "Approximate TODOs: %d\n"
		"Total time used on coding ZEsarUX: ^^%d^^ hours (and growing)\n"
		"ZEsarUX yesterday users: %s\n"

		"\n"
		"Edited with Visual Studio Code and vim\n"
		"Developed on macOS Catalina, Debian 12, Raspbian, FreeBSD 12, and MinGW environment on Windows\n"
		,
        LINES_SOURCE,
        TOTAL_COMMENTS,
        TOTAL_TODO_ITEMS,
        tiempo_trabajado_en_zesarux,
		(stats_last_yesterday_users[0]==0 ? "Unknown" : stats_last_yesterday_users)

		);

}


void menu_about_running_info(MENU_ITEM_PARAMETERS)
{

	char string_video_drivers[1024];
	char string_audio_drivers[1024];
	int driver;

	int i;

	i=0;
        for (driver=0;driver<num_scr_driver_array;driver++) {
		sprintf(&string_video_drivers[i],"%s ",scr_driver_array[driver].driver_name);
		i=i+strlen(scr_driver_array[driver].driver_name)+1;
	}
	string_video_drivers[i]=0;


	i=0;
        for (driver=0;driver<num_audio_driver_array;driver++) {
                sprintf(&string_audio_drivers[i],"%s ",audio_driver_array[driver].driver_name);
                i=i+strlen(audio_driver_array[driver].driver_name)+1;
        }
        string_audio_drivers[i]=0;



				int uptime_seconds=timer_get_uptime_seconds();

				char hora_inicio[100];
				timer_get_texto_time(&zesarux_start_time,hora_inicio);




    int media_cpu=sensor_get_percentaje_value("total_avg_cpu");

	char mensaje_cpu_usage[100];

    //por defecto
    mensaje_cpu_usage[0]=0;

    //Uso cpu no se ve en windows
#ifndef MINGW
	if (screen_show_cpu_usage.v) {
		sprintf(mensaje_cpu_usage,"Total Average CPU Use: %d%%\n",media_cpu);
	}
#endif

	char mensaje_total_uptime[100];

	//tiempo total de uso del emulador solo si esta guardado de config
	if (save_configuration_file_on_exit.v) {
		sprintf (mensaje_total_uptime,"Total minutes use %d mins\n",
  		stats_get_current_total_minutes_use() );
	}
	else {
		mensaje_total_uptime[0]=0;
	}

	//guardamos directorio actual
	char directorio_actual[PATH_MAX];
	getcwd(directorio_actual,PATH_MAX);

	menu_generic_message_format("Running info",
		"Video Driver: %s\nAvailable video drivers: %s\n\nAudio Driver: %s\nAvailable audio drivers: %s\n\n"
		"Current directory: %s\n\n"
		"Executable path: %s\n\n"
        "OS: %s\n"
		"Start time: %s\n"
		"Uptime %d secs (%d mins)\n"
		"%s"
		"%s"
		,
		scr_new_driver_name,string_video_drivers,audio_new_driver_name,string_audio_drivers,
		directorio_actual,
        zesarux_path_location,
        os_release_name,
		hora_inicio,
		uptime_seconds,uptime_seconds/60,mensaje_total_uptime,mensaje_cpu_usage);

	//Average CPU use solo sale si screen_show_cpu_usage.v




}

void menu_about_about_load_editionamegame(void)
{
    if (si_existe_editionnamegame(NULL)) {
        util_load_editionnamegame();
        salir_todos_menus=1;
    }
    else {
        //Se ha seleccionado texto edition name pero el juego no esta disponible
        debug_printf(VERBOSE_INFO,"Edition name game %s is not available",EMULATOR_GAME_EDITION);
    }
}

void menu_about_about(MENU_ITEM_PARAMETERS)
{

	char mensaje_about[1024];
	//unsigned char letra_enye,letra_e_acentuada,letra_a_acentuada,letra_o_acentuada;
	unsigned char letra_enye;

    //TODO: esto tambien sale con zx desktop cerrado en drivers video completos,
    //no aparecen acentos
    //Podria usar acentos (e incluso eñe) todo utf , y sale bien (en curses simplemente sale sin acentos y la eñe es una ene)
    //pero en curses detecta que los caracteres ocupan mas de lo que ocupan realmente y el apellido queda en una linea inferior

	if (si_complete_video_driver() ) {
		//mensaje completo con enye en segundo apellido y acentos
        //TODO: arreglar los charset para que los acentos sean letras igual en cada charset con acento
        //TODO: hacer lo mismo cuando zx desktop activo (menu_about_new)
		letra_enye=129;
        //letra_e_acentuada=143;
        //letra_a_acentuada=142;
        //letra_o_acentuada=145;
	}

	else {
		//mensaje con n en vez de enye, y acentos
		letra_enye='n';
        //letra_e_acentuada='e';
        //letra_a_acentuada='a';
        //letra_o_acentuada='o';
	}

	sprintf (mensaje_about,"ZEsarUX " EMULATOR_VERSION " (" EMULATOR_SHORT_DATE ")\n"
        " - " EMULATOR_EDITION_NAME " - \n"
        "(C) 2013 Cesar Hernandez Ba%co\n"
        //"(C) 2013 C%csar Hern%cndez Ba%c%c\n"

#ifdef SNAPSHOT_VERSION
        "Build number: " BUILDNUMBER "\n"
#endif


        ,letra_enye);
        //,letra_e_acentuada,letra_a_acentuada,letra_enye,letra_o_acentuada);





	generic_message_tooltip_return retorno_ventana;

	zxvision_generic_message_tooltip("About" , 0, 0 ,0,0,0,&retorno_ventana,0,0, mensaje_about);



	//Si se sale con ESC
    if (retorno_ventana.estado_retorno==0) return;

	//Linea seleccionada es 1? quiere decir que se selecciona texto "--- edition"
/*
	Por defecto, linea seleccionada es 0, incluso aunque no se haya habilitado linea de cursor, por ejemplo
	al buscar texto con f y n
	Como la que buscamos es la 1, no hay problema de falso positivo
*/
	int linea=retorno_ventana.linea_seleccionada;

	//printf ("retorno ventana linea: %d\n",retorno_ventana.linea_seleccionada);

	debug_printf(VERBOSE_INFO,"Closing window with Enter and selected line=%d",linea);
	if (linea==1) {
		menu_about_about_load_editionamegame();
	}

}


void menu_about_compile_info(MENU_ITEM_PARAMETERS)
{
        char buffer[MAX_COMPILE_INFO_LENGTH];
        get_compile_info(buffer);

	menu_generic_message("Compile info",
		buffer
	);
}


void menu_about_help(MENU_ITEM_PARAMETERS)
{

	if (!menu_cond_stdout() ) {
		menu_generic_message("Help",
            "On Menu:\n"
			"Use cursor keys to move\n"
			"Press F1 (or h on some video drivers) on an option to see its help\n"
			"Press Enter or cursor right to select a menu item\n"
			"Press Space on some menu items to enable/disable\n"
			"Press a key between a and z to select an entry with shortcuts; shortcut clues appear when you wait some seconds or press a key "
			"not associated with any shortcut.\n"
			"Unavailable options may be hidden, or disabled, which are shown with red colour or with x cursor on some video drivers\n"
            "Character > on the right of a menu item means the menu has submenus\n"
			"ESC Key closes all menus, except in the case with aalib driver and pure text console, which is changed to another key (shown on the menu). On curses driver, ESC key is a bit slow, you have to wait one second after pressing it; you can also use key @ to simulate ESC on menu on curses driver.\n"
            "Cursor left gives you to the previous menu\n"
			"\n\n"
			"On File Browser:\n"
			"- Use cursors and PgDn/Up\n"
			"- Use Enter or left mouse click to select item. Compressed files will be opened like folders\n"
			"- Use Space to expand files, currently supported:\n"
            "ddh, dsk, epr, eprom, flash, hdf, mdv, o, p, pzx, scl, tap, trd, tzx and also all the compressed supported files.\n"
            "- When moving the cursor, a small preview of the screen in the file is shown, currently supported files for preview:\n"
            "ddh, dsk, p, pzx, scr, sna, sp, tap, trd, tzx, z80, zsf\n"
			"- Use TAB to change section\n"
			"- Use Space/cursor on filter to change filter\n"
			"- Press the initial letter \n"
			"for faster searching\n"
            "\n"
			"When using fileselector from file utilities menu:\n"
			"- Press shift+key to select functions, like shift+v to view files or shift+d to select Drive\n"
            "- Currently supported files on Viewer:\n"
            "b, bas, basz88, baszx80, baszx81, cas, cdt, col, ddh, dsk, epr, eprom, flash, "
            "ide, mmc, mmcide, nex, o, p, pzx, sna, snx, sp, spg, tap, trd, tzx, z80, zsf, zx and text files.\n"
			"\n"
			"On message windows:\n"
			"- Use cursors and PgDn/Up\n"
			"- Use f and n to find text\n"
			"- Use c to copy to ZEsarUX clipboard\n"
			"\n"
            "On input fields, press cursor down to delete all input text.\n"
			"On numeric input fields, numbers can be written on decimal, hexadecimal (with suffix H), binary (with suffix %) or as a character (with quotes '' or \"\")\n\n"
			"Symbols on menu must be written according to the Spectrum keyboard mapping, so for example, to write the symbol minus (<), you have to press "
			"ctrl(symbol shift)+r. You should use ctrl/alt (no need to Spectrum extended mode) to write any of the following: ~|\\{}[], located on letters asdfgyu\n\n"
			"\n"
			"On ZX Vision windows:\n"
			"- Use mouse to move windows dragging from the title bar\n"
			"- Drag mouse from the bottom-right part of the window to resize it\n"
            "- Click on the minimize button (-) to minimize the window\n"
			"- Click on the maximize button (+) or double click on the title bar to maximize the window\n"
            "- Click on the recover button (=) to recover previous size after a minimize or maximize action\n"
            "- Click out of the window to close all menus\n"
			"- Click out of the window and press shift key to put the focus on the emulated machine and send there keyboard presses\n"
			"- Can also be moved with the keyboard: Shift+QAOP\n"
			"- Can also be resized with the keyboard: Shift+WSKL\n"
            "- Note: If the resize zone of the window (bottom-right) flashes in red colour, means that the window needs "
            "to be enlarged, because there is text beyond current size.\n"

			"\n"

			"On ZX Desktop:\n"
			"- Left mouse button click on an icon to open it or trigger an action\n"
			"- Right mouse button click on an icon to get properties or actions of that icon; "
            "right click on the ZX Desktop for icon actions or desktop properties\n"

			"\n"

			"Inside a machine, the keys are mapped this way:\n"
			"ESC: If text to speech is not enabled, sends Shift+Space (break) on Spectrum. If enabled, stops playing text to speech\n"
			"CTRL/ALT: Symbol shift\n"
			"TAB: Extended mode (symbol shift + caps shift)\n"
			"\n"
			"F4: When menu is closed, send Spectrum display content to speech program, including only known characters (unknown characters are shown as a space). "
			"When menu is opened, it will read the window contents and send it to speech program, even if it has already been read again\n"
			"F5: Open menu\n"
			"F8: Open On Screen Keyboard (only on Spectrum & ZX80/81)\n"
			"F9: Open Smart Load window\n"
			"\n"
			"On Z88, the following keys have special meaning:\n"
			"F1: Help\n"
			"F2: Index\n"
			"F3: Menu\n"
			"CTRL: Diamond\n"
			"ALT: Square\n"
			"\n"
			"Note: Drivers curses, aa, caca do not read CTRL or ALT. Instead, you can use F6 for CTRL and F7 for ALT on these drivers.\n"
	);
	}

	else {
		menu_generic_message("Help","Press h<num> to see help for <num> option, for example: h3\n"
				"Press t<num> to see tooltip for <num> option\n"
				"Unavailable options may be hidden, or disabled, which are shown with x cursor\n"
				"ESC text gives you to the previous menu\n"
				"On fileselector, write the file without spaces\n"
				"On input fields, numbers can be written on decimal or hexadecimal (with suffix H)\n"
	);
	}

}


void menu_help_background_windows(MENU_ITEM_PARAMETERS)
{


		menu_generic_message("Background Windows Help",
			"When you enable Background Windows (from Settings-> ZX Vision), "
			"some features are added to the ZX Desktop and some behaviour is modified. \n"
			"\n"
			"First, we will refer to 'menu' to all the menu windows and all the windows in background. "
			"When we say that the 'menu is open' or the 'menu is closed', we refer to all the menu windows and all the windows "
			"in background, that they are visible and can be interacted with them or not "
            "(enabling setting background windows+even when menu closed, shows windows in background but you can't interact with them). \n"
			"\n"
			"We will refer to 'main menu' to the main ZEsarUX menu window (the one with Smartload, Machine, etc). \n"
			"\n"
			"Windows that can be sent to background, have a button (exclamation mark !, or a shaded button) in the window title. \n"
			"Pressing that button (or F6 key) will send the window to the background and then, "
            "if background windows setting is enabled even when menu closed, the menu will be closed; otherwise, it will go back to the previous window. \n"
			"\n"
			"A window in the background is open, doesn't have the focus, and usually is refreshing its contents "
			"(we will talk about it later). \n"
			"If you click to another window, the current window will be sent to background, if it allows to do it. "
			"If it's not allowed, the current window will be closed. \n"
			"Some background windows don't refresh their contents when they are in background, like Debug CPU. \n"
			"\n"
			"Pressing F5 when on menu or pressing the ZEsarUX logo button (on the top left of ZX Desktop) will send you back to the main menu.\n"
            "Pressing Shift+Cursor Right will switch to another background window.\n"
			"\n"

			"Pressing window close button, ESC or right click on the mouse it always closes the current window (same behaviour with background windows disabled). \n"
			"\n"
			"When menu is open, pressing on the emulated machine section (usually on the left) and shift key, the keyboard focus is lost from the menu "
			"and goes to the emulated machine. To return the keyboard focus to the menu, press on any of the background windows, "
			"or in the ZEsarUX logo, or press F5. \n"
			"\n"
			"Keep in mind that F-keys are only read when menu is closed (except F4, F5 and F6, which are also read with menu open). "
			"\n"
			"You can enable a setting to restore windows when ZEsarUX is opened (from Settings-> ZX Vision-> "
			"Reopen windows on start). \n"
			"\n"
            "You can enable a setting to view windows in background even when menu closed (but you can't interact with them), "
            "from Settings-> ZX Vision-> Background Windows-Even when menu closed\n"
            "When setting Background Windows-Even is disabled, pressing ESC on the main menu, makes disappear all windows. But pressing F5 again will restore all windows. \n"
			"You can have background windows enabled but multitasking disabled (not recommended, but allowed). "


	);


}


void menu_licenses(MENU_ITEM_PARAMETERS)
{
        menu_item *array_menu_common;
        menu_item item_seleccionado;
        int retorno_menu;
        do {

            //En este menu no hago que al abrir un item se cierren todos los menus (usando menu_add_item_menu_se_cerrara(array_menu_common); ),
            //porque creo que puede ser lógico que si un usuario lee una licencia, quiera leer las demás, y así no se le cierra el menu
            //y las tiene todas a mano

            menu_add_item_menu_inicial(&array_menu_common,"~~Information",MENU_OPCION_NORMAL,menu_about_licenses_info,NULL);
			menu_add_item_menu_shortcut(array_menu_common,'i');

            menu_add_item_menu(array_menu_common,"~~ZEsarUX",MENU_OPCION_NORMAL,menu_about_license,NULL);
			menu_add_item_menu_shortcut(array_menu_common,'z');

			menu_add_item_menu(array_menu_common,"~~Atomlite (from simcoupe)",MENU_OPCION_NORMAL,menu_about_license_atomlite,NULL);
			menu_add_item_menu_shortcut(array_menu_common,'a');

			menu_add_item_menu(array_menu_common,"Disassembler (from ~~fuse)",MENU_OPCION_NORMAL,menu_about_license_fuse,NULL);
			menu_add_item_menu_shortcut(array_menu_common,'f');

			menu_add_item_menu(array_menu_common,"D~~OS font",MENU_OPCION_NORMAL,menu_about_license_dosfont,NULL);
			menu_add_item_menu_shortcut(array_menu_common,'o');

			menu_add_item_menu(array_menu_common,"Fa~~tFs",MENU_OPCION_NORMAL,menu_about_license_fatfs,NULL);
			menu_add_item_menu_shortcut(array_menu_common,'t');

			menu_add_item_menu(array_menu_common,"~~grackle",MENU_OPCION_NORMAL,menu_about_license_grackle,NULL);
			menu_add_item_menu_shortcut(array_menu_common,'g');

			menu_add_item_menu(array_menu_common,"~~HiLow",MENU_OPCION_NORMAL,menu_about_license_hilow,NULL);
			menu_add_item_menu_shortcut(array_menu_common,'h');

			menu_add_item_menu(array_menu_common,"Mac OS video driver (from ~~Qemu)",MENU_OPCION_NORMAL,menu_about_license_qemu,NULL);
			menu_add_item_menu_shortcut(array_menu_common,'q');

			menu_add_item_menu(array_menu_common,"m~~dvtool",MENU_OPCION_NORMAL,menu_about_license_mdvtool,NULL);
			menu_add_item_menu_shortcut(array_menu_common,'d');

			menu_add_item_menu(array_menu_common,"~~Motorola Core",MENU_OPCION_NORMAL,menu_about_license_motorola_core,NULL);
			menu_add_item_menu_shortcut(array_menu_common,'m');

			menu_add_item_menu(array_menu_common,"Pla~~yTZX",MENU_OPCION_NORMAL,menu_about_license_playtzx,NULL);
			menu_add_item_menu_shortcut(array_menu_common,'y');

			menu_add_item_menu(array_menu_common,"s~~cl2trd",MENU_OPCION_NORMAL,menu_about_license_scl2trd,NULL);
			menu_add_item_menu_shortcut(array_menu_common,'c');

			menu_add_item_menu(array_menu_common,"~~SCMP Core",MENU_OPCION_NORMAL,menu_about_license_scmp_core,NULL);
			menu_add_item_menu_shortcut(array_menu_common,'s');

			menu_add_item_menu(array_menu_common,"SPG ~~loader (from unrealspeccy)",MENU_OPCION_NORMAL,menu_about_license_unrealspeccy,NULL);
			menu_add_item_menu_shortcut(array_menu_common,'l');

			menu_add_item_menu(array_menu_common,"~~undaad",MENU_OPCION_NORMAL,menu_about_license_undaad,NULL);
			menu_add_item_menu_shortcut(array_menu_common,'u');

			menu_add_item_menu(array_menu_common,"u~~ngac",MENU_OPCION_NORMAL,menu_about_license_ungac,NULL);
			menu_add_item_menu_shortcut(array_menu_common,'n');

			menu_add_item_menu(array_menu_common,"unpa~~ws/unquill",MENU_OPCION_NORMAL,menu_about_license_unpaws,NULL);
			menu_add_item_menu_shortcut(array_menu_common,'w');

			menu_add_item_menu(array_menu_common,"zi~~p",MENU_OPCION_NORMAL,menu_about_license_zip,NULL);
			menu_add_item_menu_shortcut(array_menu_common,'p');


            menu_add_item_menu_separator(array_menu_common);

            menu_add_ESC_item(array_menu_common);

            retorno_menu=menu_dibuja_menu_no_title_lang(&licenses_opcion_seleccionada,&item_seleccionado,array_menu_common,"Licenses" );



            if ((item_seleccionado.tipo_opcion&MENU_OPCION_ESC)==0 && retorno_menu>=0) {
            	//llamamos por valor de funcion
                if (item_seleccionado.menu_funcion!=NULL) {
                	//printf ("actuamos por funcion\n");
                    item_seleccionado.menu_funcion(item_seleccionado.valor_opcion);

                }
            }

        } while ( (item_seleccionado.tipo_opcion&MENU_OPCION_ESC)==0 && retorno_menu!=MENU_RETORNO_ESC && !salir_todos_menus);

}

int if_menu_new_about_is_available(void)
{
    //No hacer si no hay driver grafico completo
    if (!si_complete_video_driver()) return 0;

    //Solo cuando zx desktop activado y soportado por el driver
    if (!if_zxdesktop_enabled_and_driver_allows()) return 0;

    return 1;
}

void menu_about_about_which(MENU_ITEM_PARAMETERS)
{
    if (if_menu_new_about_is_available()) menu_about_new(0);
    else menu_about_about(0);
}


void menu_in_memoriam_david_about(MENU_ITEM_PARAMETERS)
{

    if (gui_language==GUI_LANGUAGE_SPANISH || gui_language==GUI_LANGUAGE_CATALAN) {
        menu_about_read_file("In Memoriam David","IN_MEMORIAM_DAVID_es",1);
    }
    else menu_about_read_file("In Memoriam David","IN_MEMORIAM_DAVID",1);

    salir_todos_menus=1;
}

void menu_in_memoriam_david_software(MENU_ITEM_PARAMETERS)
{

    //localizar archivo o carpeta
    char buffer_nombre[PATH_MAX];


    int existe=find_sharedfile("my_soft/bitsoft/bitsoft.zmenu",buffer_nombre);

    if (!existe) {
        debug_printf(VERBOSE_ERR,"Can not find software");
        return;
    }

    zmenu_parse_file(buffer_nombre);

    salir_todos_menus=1;
}

void menu_in_memoriam_david(MENU_ITEM_PARAMETERS)
{
    menu_item *array_menu_common;
    menu_item item_seleccionado;
    int retorno_menu;


    do {

        menu_add_item_menu_en_es_ca_inicial(&array_menu_common,MENU_OPCION_NORMAL,menu_in_memoriam_david_about,NULL,
            "~~About","~~Acerca de","Quant ~~a");
        menu_add_item_menu_shortcut(array_menu_common,'a');
        menu_add_item_menu_genera_ventana(array_menu_common);

        menu_add_item_menu_en_es_ca(array_menu_common,MENU_OPCION_NORMAL,menu_in_memoriam_david_software,NULL,
            "~~Software from David","~~Software de David","~~Software de David");
        menu_add_item_menu_shortcut(array_menu_common,'s');
        menu_add_item_menu_genera_ventana(array_menu_common);

        menu_add_item_menu_separator(array_menu_common);

        menu_add_ESC_item(array_menu_common);

        retorno_menu=menu_dibuja_menu_no_title_lang(&in_memoriam_david_opcion_seleccionada,&item_seleccionado,array_menu_common,"In Memoriam David");



        if ((item_seleccionado.tipo_opcion&MENU_OPCION_ESC)==0 && retorno_menu>=0) {
            //llamamos por valor de funcion
            if (item_seleccionado.menu_funcion!=NULL) {
                //printf ("actuamos por funcion\n");
                item_seleccionado.menu_funcion(item_seleccionado.valor_opcion);

            }
        }

    } while ( (item_seleccionado.tipo_opcion&MENU_OPCION_ESC)==0 && retorno_menu!=MENU_RETORNO_ESC && !salir_todos_menus);

}

void menu_in_memoriam_diego_software(MENU_ITEM_PARAMETERS)
{

    //localizar archivo o carpeta
    char buffer_nombre[PATH_MAX];


    int existe=find_sharedfile("my_soft/microbyt",buffer_nombre);

    if (!existe) {
        debug_printf(VERBOSE_ERR,"Can not find software");
        return;
    }

    //Ponemos la barra del final para que entre en la carpeta
    sprintf(quickload_file,"%s/",buffer_nombre);

    //y abrimos menu de smartload
    quickfile=quickload_file;
    menu_smartload(0);


    salir_todos_menus=1;
}

void menu_in_memoriam_diego_about(MENU_ITEM_PARAMETERS)
{

    if (gui_language==GUI_LANGUAGE_SPANISH || gui_language==GUI_LANGUAGE_CATALAN) {
        menu_about_read_file("In Memoriam Diego","IN_MEMORIAM_DIEGO_es",1);
    }
    else menu_about_read_file("In Memoriam Diego","IN_MEMORIAM_DIEGO",1);

    salir_todos_menus=1;
}

void menu_in_memoriam_diego(MENU_ITEM_PARAMETERS)
{
    menu_item *array_menu_common;
    menu_item item_seleccionado;
    int retorno_menu;


    do {

        menu_add_item_menu_en_es_ca_inicial(&array_menu_common,MENU_OPCION_NORMAL,menu_in_memoriam_diego_about,NULL,
            "~~About","~~Acerca de","Quant ~~a");
        menu_add_item_menu_shortcut(array_menu_common,'a');
        menu_add_item_menu_genera_ventana(array_menu_common);

        menu_add_item_menu_en_es_ca(array_menu_common,MENU_OPCION_NORMAL,menu_in_memoriam_diego_software,NULL,
            "~~Software from Diego & Oscar","~~Software de Diego & Oscar","~~Software de Diego & Oscar");
        menu_add_item_menu_shortcut(array_menu_common,'s');
        menu_add_item_menu_genera_ventana(array_menu_common);

        menu_add_item_menu_separator(array_menu_common);

        menu_add_ESC_item(array_menu_common);

        retorno_menu=menu_dibuja_menu_no_title_lang(&in_memoriam_diego_opcion_seleccionada,&item_seleccionado,array_menu_common,"In Memoriam Diego");



        if ((item_seleccionado.tipo_opcion&MENU_OPCION_ESC)==0 && retorno_menu>=0) {
            //llamamos por valor de funcion
            if (item_seleccionado.menu_funcion!=NULL) {
                //printf ("actuamos por funcion\n");
                item_seleccionado.menu_funcion(item_seleccionado.valor_opcion);

            }
        }

    } while ( (item_seleccionado.tipo_opcion&MENU_OPCION_ESC)==0 && retorno_menu!=MENU_RETORNO_ESC && !salir_todos_menus);

}





void menu_in_memoriam_about(MENU_ITEM_PARAMETERS)
{
    if (gui_language==GUI_LANGUAGE_SPANISH || gui_language==GUI_LANGUAGE_CATALAN) {
        menu_generic_message_format("About In Memoriam",
            "Te estarás preguntando... que es esto? Pues es una manera de homenajear a los que ya no están con nosotros, "
            "a personas que han sido importantes en mi vida y han tenido relación con ZEsarUX.\n"
            "Puede parecerte algo extraño que un programa contenga esta sección (o no?) pero es una manera que tengo de homenajearlos.\n"
            "Espero que esta sección no aumente con los años, cosa que sería buena señal."

        );
    }

    else {
        menu_generic_message_format("About In Memoriam",
            "You might be wondering... what is this? Well, it's a way to honor those who are no longer with us, "
             "to people who have been important in my life and have had a relationship with ZEsarUX.\n"
             "It may seem strange to you that a program contains this section (or not?) but it is a way I have to honor them.\n"
             "I hope this section does not increase over the years, which would be a good sign."
        );
    }
}

void menu_in_memoriam(MENU_ITEM_PARAMETERS)
{
    menu_item *array_menu_common;
    menu_item item_seleccionado;
    int retorno_menu;


    do {

        menu_add_item_menu_inicial_format(&array_menu_common,MENU_OPCION_NORMAL,menu_in_memoriam_about,NULL,"~~About In Memoriam");
        menu_add_item_menu_shortcut(array_menu_common,'a');

        menu_add_item_menu_format(array_menu_common,MENU_OPCION_NORMAL,menu_in_memoriam_diego,NULL,"~~Diego");
        menu_add_item_menu_shortcut(array_menu_common,'d');
        menu_add_item_menu_tiene_submenu(array_menu_common);

        menu_add_item_menu_format(array_menu_common,MENU_OPCION_NORMAL,menu_in_memoriam_david,NULL,"D~~avid");
        menu_add_item_menu_shortcut(array_menu_common,'a');
        menu_add_item_menu_tiene_submenu(array_menu_common);

        menu_add_item_menu_separator(array_menu_common);

        menu_add_ESC_item(array_menu_common);

        retorno_menu=menu_dibuja_menu_no_title_lang(&in_memoriam_opcion_seleccionada,&item_seleccionado,array_menu_common,"In Memoriam");



        if ((item_seleccionado.tipo_opcion&MENU_OPCION_ESC)==0 && retorno_menu>=0) {
            //llamamos por valor de funcion
            if (item_seleccionado.menu_funcion!=NULL) {
                //printf ("actuamos por funcion\n");
                item_seleccionado.menu_funcion(item_seleccionado.valor_opcion);

            }
        }

    } while ( (item_seleccionado.tipo_opcion&MENU_OPCION_ESC)==0 && retorno_menu!=MENU_RETORNO_ESC && !salir_todos_menus);
}

//Para poner el texto de bienvenida asi como un contador
char start_wizard_window_title[ZXVISION_MAX_WINDOW_TITLE];

//Total de preguntas sin tener en cuenta los condicionales (como el de zx desktop)
int start_wizard_total_steps=7;


void menu_first_start_wizard_language(MENU_ITEM_PARAMETERS)
{

    //Nota: estoy habilitando aqui algunas opciones que en ZEsarUX no vienen activadas por defecto (como visibilidad de tooltips por ejemplo)

    //Textos en los diferentes idiomas
    char *mensaje_zxdesktop_english="ZX Desktop allows you to enlarge the main window by adding a Desktop area "
        "so you can have icons and menus located there. Do you want to enable ZX Desktop?";
    char *mensaje_zxdesktop_spanish="ZX Desktop permite agrandar la ventana principal añadiendo una zona de escritorio "
        "de tal manera que puedes tener iconos y menús localizados ahí. Quieres activar ZX Desktop?";
    char *mensaje_zxdesktop_catalan="ZX Desktop permet engrandir la finestra principal afegint una zona d'escriptori "
        "de tal manera que pots tenir icones i menús localizats allà. Vols activar ZX Desktop?";

    char *mensaje_autosnapshot_spanish="Quieres autograbar el estado de la máquina emulada (snapshot) al salir? "
        "Y se restaura automáticamente al inicio";
    char *mensaje_autosnapshot_catalan="Vols autogravar l'estat de la màquina emulada (snapshot) al sortir? "
        "I es restaura automàticament a l'inici";
    char *mensaje_autosnapshot_english="Do you want to autosave the emulated machine state (snapshot) on exit? "
        "And it will be restored automatically on start";


    char *mensaje_realvideo_spanish="Quieres activar Real Video? Es una opción para emular a la perfección los efectos de pantalla de la máquina emulada. "
        "Puede usar un poco más de CPU pero generalmente es conveniente activarlo";
    char *mensaje_realvideo_catalan="Vols activar Real Video? Es una opció per emular a la perfecció els efectes de pantalla de la màquina emulada. "
        "Pot usar una mica més de CPU però habitualment és convenient activar-ho";
    char *mensaje_realvideo_english="Do you want to enable Real Video? It's an option to perfectly emulate the screen effects of the emulated machine. "
        "It may use a little more CPU, but it's generally a good idea to enable it";

    char *mensaje_firstaid_spanish="Quieres activar mensajes de ayuda de 'Primeros Auxilios'? Son mensajes de ayuda que aparecen al iniciar ZEsarUX "
        "y también al abrir ciertos menús";
    char *mensaje_firstaid_catalan="Vols activar missatges d'ajuda de 'Primers Auxilis'? Son missatges d'ajuda que apareixen al iniciar ZEsarUX "
        "i també al obrir alguns menús";
    char *mensaje_firstaid_english="Do you want to enable 'First aid' help messages? They are help messages that appear on starting ZEsarUX "
        "and also when opening some menus";

    char *mensaje_tooltips_spanish="Quieres activar mensajes de ayuda 'Tooltips'? Son mensajes de ayuda que aparecen al elegir entradas de menús";
    char *mensaje_tooltips_catalan="Vols activar missatges d'ajuda 'Tooltips'? Son missatges d'ajuda que apareixen al escollir entrades de menús";
    char *mensaje_tooltips_english="Do you want to enable 'Tooltips' help messages? They are help messages that appear on selecting menu items";


    char *mensaje_atajos_spanish="Quieres activar la visualización de los atajos de teclado? Permite seleccionar entradas de menú pulsando simplemente una tecla";
    char *mensaje_atajos_catalan="Vols activar la visualització de les dreçeres de teclat? Permet seleccionar entrades de menú prement simplement una tecla";
    char *mensaje_atajos_english="Do you want to enable visible shortcuts? It allows to select menu entries by pressing a key";

    char *mensaje_fin_spanish="Finalizado el asistente inicial, puedes empezar a usar ZEsarUX.\n"
        "Para abrir el menú, puedes usar F5, pulsar el botón izquierdo del ratón sobre el fondo, o pulsar en alguno de los iconos de menú "
        "superiores si has habilitado ZX Desktop.\n"
        "Puedes ahora cerrar esta ventana pulsando ESC, ENTER o haciendo click izquierdo con el ratón "
        "en el botón superior izquierdo de la ventana. Que lo disfrutes!";

    char *mensaje_fin_catalan="Finalitzat l'assistent inicial, pots començar a utilitzar ZEsarUX.\n"
        "Per obrir el menú, pots fer servir F5, prémer el botó esquerre del ratolí sobre el fons, o prémer alguna de les icones de menú "
        "superiors si has habilitat ZX Desktop.\n"
        "Pots ara tancar aquesta finestra prement ESC, ENTER o fent clic esquerre amb el ratolí "
        "al botó superior esquerre de la finestra. Que ho gaudeixis!";

    char *mensaje_fin_english="After completing the initial wizard, you can start using ZEsarUX.\n"
        "To open the menu, you can use F5, left-click on the background, or click on one of the menu icons at the top "
        "if you have enabled ZX Desktop.\n"
        "You can now close this window by pressing ESC, ENTER, or left-clicking the mouse "
        "on the top left button of the window. Enjoy!";


    //Punteros a los mensajes del idioma correspondiente
    char *mensaje_zxdesktop;
    mensaje_zxdesktop=mensaje_zxdesktop_english;

    char *mensaje_realvideo;
    mensaje_realvideo=mensaje_realvideo_english;

    char *mensaje_autosnapshot;
    mensaje_autosnapshot=mensaje_autosnapshot_english;

    char *mensaje_firstaid;
    mensaje_firstaid=mensaje_firstaid_english;

    char *mensaje_tooltips;
    mensaje_tooltips=mensaje_tooltips_english;

    char *mensaje_atajos;
    mensaje_atajos=mensaje_atajos_english;

    char *mensaje_fin;
    mensaje_fin=mensaje_fin_english;

    int current_step=2;

    switch (valor_opcion) {
        case 0:
            //Aunque este es el lenguaje por defecto, pero por si acaso lo definimos aqui
            gui_language=GUI_LANGUAGE_DEFAULT;
            mensaje_zxdesktop=mensaje_zxdesktop_english;
            mensaje_realvideo=mensaje_realvideo_english;
            mensaje_autosnapshot=mensaje_autosnapshot_english;
            mensaje_firstaid=mensaje_firstaid_english;
            mensaje_tooltips=mensaje_tooltips_english;
            mensaje_atajos=mensaje_atajos_english;
            mensaje_fin=mensaje_fin_english;
        break;

        case 1:
            gui_language=GUI_LANGUAGE_SPANISH;
            mensaje_zxdesktop=mensaje_zxdesktop_spanish;
            mensaje_realvideo=mensaje_realvideo_spanish;
            mensaje_autosnapshot=mensaje_autosnapshot_spanish;
            mensaje_firstaid=mensaje_firstaid_spanish;
            mensaje_tooltips=mensaje_tooltips_spanish;
            mensaje_atajos=mensaje_atajos_spanish;
            mensaje_fin=mensaje_fin_spanish;
        break;

        case 2:
            gui_language=GUI_LANGUAGE_CATALAN;
            mensaje_zxdesktop=mensaje_zxdesktop_catalan;
            mensaje_realvideo=mensaje_realvideo_catalan;
            mensaje_autosnapshot=mensaje_autosnapshot_catalan;
            mensaje_firstaid=mensaje_firstaid_catalan;
            mensaje_tooltips=mensaje_tooltips_catalan;
            mensaje_atajos=mensaje_atajos_catalan;
            mensaje_fin=mensaje_fin_catalan;
        break;
    }



    //Preguntar si realvideo
    int opcion_activar_realvideo=1;
    sprintf(start_wizard_window_title,"%s %d/%d",menu_get_string_language("Welcome"),current_step++,start_wizard_total_steps);
    zxvision_menu_generic_message_setting(start_wizard_window_title,mensaje_realvideo,
        menu_get_string_language("Enabled"),&opcion_activar_realvideo);

    if (salir_todos_menus) return;

    if (opcion_activar_realvideo) {
         if (rainbow_enabled.v==0) enable_rainbow();
    }


    //Preguntar si autoguardado/autocarga snapshot
    int opcion_autosnapshot=1;
    sprintf(start_wizard_window_title,"%s %d/%d",menu_get_string_language("Welcome"),current_step++,start_wizard_total_steps);
    zxvision_menu_generic_message_setting(start_wizard_window_title,mensaje_autosnapshot,
        menu_get_string_language("Enabled"),&opcion_autosnapshot);

    if (salir_todos_menus) return;

    autosave_snapshot_on_exit.v=opcion_autosnapshot;
    autoload_snapshot_on_start.v=opcion_autosnapshot;


    //Preguntar si first aid
    int opcion_activar_first_aid=1;
    sprintf(start_wizard_window_title,"%s %d/%d",menu_get_string_language("Welcome"),current_step++,start_wizard_total_steps);
    zxvision_menu_generic_message_setting(start_wizard_window_title,mensaje_firstaid,
        menu_get_string_language("Enabled"),&opcion_activar_first_aid);

    if (salir_todos_menus) return;

    if (!opcion_activar_first_aid) {
        menu_disable_first_aid.v=1;
    }


    //Preguntar si tooltips
    int opcion_activar_tooltips=0;
    sprintf(start_wizard_window_title,"%s %d/%d",menu_get_string_language("Welcome"),current_step++,start_wizard_total_steps);
    zxvision_menu_generic_message_setting(start_wizard_window_title,mensaje_tooltips,
        menu_get_string_language("Enabled"),&opcion_activar_tooltips);

    if (salir_todos_menus) return;

    tooltip_enabled.v=opcion_activar_tooltips;


    //Preguntar si visibilidad atajos teclado
    int opcion_activar_atajos=1;
    sprintf(start_wizard_window_title,"%s %d/%d",menu_get_string_language("Welcome"),current_step++,start_wizard_total_steps);
    zxvision_menu_generic_message_setting(start_wizard_window_title,mensaje_atajos,
        menu_get_string_language("Enabled"),&opcion_activar_atajos);

    if (salir_todos_menus) return;

    menu_force_writing_inverse_color.v=opcion_activar_atajos;


    //Preguntar si ZX Desktop. Este al final para no marear mucho al usuario con el cambio de posicion de la ventana
    if (scr_driver_can_ext_desktop() ) {
        int opcion_activar_zxdesktop=1;
        sprintf(start_wizard_window_title,"%s %d/%d",menu_get_string_language("Welcome"),current_step++,start_wizard_total_steps);
        zxvision_menu_generic_message_setting(start_wizard_window_title,mensaje_zxdesktop,
            menu_get_string_language("Enabled"),&opcion_activar_zxdesktop);

        if (salir_todos_menus) return;

        if (opcion_activar_zxdesktop) {
            //No esta habilitado, pero por si acaso recomprobar
            if (!screen_ext_desktop_enabled) {
                enable_zxdesktop_and_background();
            }
        }
    }


    //Final del asistente
    sprintf(start_wizard_window_title,"%s %d/%d",menu_get_string_language("Welcome"),current_step++,start_wizard_total_steps);
    menu_generic_message(start_wizard_window_title,mensaje_fin);
    salir_todos_menus=1;

}

/*
void menu_first_start_wizard_exit(MENU_ITEM_PARAMETERS)
{
    salir_todos_menus=1;
}
*/

//Inicio preguntando el idioma
void menu_first_start_wizard(void)
{

    //al aparecer el wizard no mostramos el first aid para no molestar demasiado al usuario
    menu_first_aid_must_show_startup=0;

    menu_item *array_menu_common;
    menu_item item_seleccionado;
    int retorno_menu;

    if (scr_driver_can_ext_desktop() ) start_wizard_total_steps++;

    do {

        menu_add_item_menu_inicial_format(&array_menu_common,MENU_OPCION_NORMAL,menu_first_start_wizard_language,NULL,"Select English Language");
        menu_add_item_menu_valor_opcion(array_menu_common,0);

        menu_add_item_menu_format(array_menu_common,MENU_OPCION_NORMAL,menu_first_start_wizard_language,NULL,"Elige idioma Español");
        menu_add_item_menu_valor_opcion(array_menu_common,1);

        menu_add_item_menu_format(array_menu_common,MENU_OPCION_NORMAL,menu_first_start_wizard_language,NULL,"Escull idioma Català");
        menu_add_item_menu_valor_opcion(array_menu_common,2);

        menu_add_item_menu_separator(array_menu_common);

        menu_add_item_menu(array_menu_common,"Cancel",MENU_OPCION_NORMAL|MENU_OPCION_ESC,NULL,NULL);

        //menu_add_item_menu_format(array_menu_common,MENU_OPCION_NORMAL,menu_first_start_wizard_exit,NULL,"Exit - Salir - Sortir");


        //menu_add_ESC_item(array_menu_common);

        sprintf(start_wizard_window_title,"Welcome 1/%d",start_wizard_total_steps);

        retorno_menu=menu_dibuja_menu_no_title_lang(&in_memoriam_opcion_seleccionada,&item_seleccionado,array_menu_common,start_wizard_window_title);



        if ((item_seleccionado.tipo_opcion&MENU_OPCION_ESC)==0 && retorno_menu>=0) {
            //llamamos por valor de funcion
            if (item_seleccionado.menu_funcion!=NULL) {
                //printf ("actuamos por funcion\n");
                item_seleccionado.menu_funcion(item_seleccionado.valor_opcion);

            }
        }

    } while ( (item_seleccionado.tipo_opcion&MENU_OPCION_ESC)==0 && retorno_menu!=MENU_RETORNO_ESC && !salir_todos_menus);
}


void menu_help(MENU_ITEM_PARAMETERS)
{
        menu_item *array_menu_common;
        menu_item item_seleccionado;
        int retorno_menu;
        do {
            menu_add_item_menu_inicial(&array_menu_common,"~~About",MENU_OPCION_NORMAL,menu_about_about_which,NULL);
            menu_add_item_menu_spanish_catalan(array_menu_common,"~~Acerca de","Quant ~~a");
			menu_add_item_menu_shortcut(array_menu_common,'a');
            menu_add_item_menu_se_cerrara(array_menu_common);
            menu_add_item_menu_genera_ventana(array_menu_common);

			menu_add_item_menu(array_menu_common,"~~Help",MENU_OPCION_NORMAL,menu_about_help,NULL);
			menu_add_item_menu_shortcut(array_menu_common,'h');
             menu_add_item_menu_se_cerrara(array_menu_common);
            menu_add_item_menu_genera_ventana(array_menu_common);

			menu_add_item_menu(array_menu_common,"~~Background Windows Help",MENU_OPCION_NORMAL,menu_help_background_windows,NULL);
			menu_add_item_menu_shortcut(array_menu_common,'b');
             menu_add_item_menu_se_cerrara(array_menu_common);
            menu_add_item_menu_genera_ventana(array_menu_common);

			menu_add_item_menu(array_menu_common,"~~Keyboard Help",MENU_OPCION_NORMAL,menu_help_show_keyboard,NULL);
			menu_add_item_menu_shortcut(array_menu_common,'k');
             menu_add_item_menu_se_cerrara(array_menu_common);
            menu_add_item_menu_genera_ventana(array_menu_common);

			menu_add_item_menu(array_menu_common,"~~Readme",MENU_OPCION_NORMAL,menu_about_readme,NULL);
			menu_add_item_menu_shortcut(array_menu_common,'r');
             menu_add_item_menu_se_cerrara(array_menu_common);
            menu_add_item_menu_genera_ventana(array_menu_common);

			menu_add_item_menu(array_menu_common,"~~Features",MENU_OPCION_NORMAL,menu_about_features,NULL);
			menu_add_item_menu_shortcut(array_menu_common,'f');
             menu_add_item_menu_se_cerrara(array_menu_common);
            menu_add_item_menu_genera_ventana(array_menu_common);

			menu_add_item_menu_en_es_ca(array_menu_common,MENU_OPCION_NORMAL,menu_about_exclusivefeatures,NULL,
                "E~~xclusive features","Características e~~xclusivas","Característiques e~~xclusives");
			menu_add_item_menu_shortcut(array_menu_common,'x');
             menu_add_item_menu_se_cerrara(array_menu_common);
            menu_add_item_menu_genera_ventana(array_menu_common);

            menu_add_item_menu(array_menu_common,"H~~istory",MENU_OPCION_NORMAL,menu_about_history,NULL);
            menu_add_item_menu_spanish(array_menu_common,"H~~istoria");
			menu_add_item_menu_shortcut(array_menu_common,'i');
             menu_add_item_menu_se_cerrara(array_menu_common);
            menu_add_item_menu_genera_ventana(array_menu_common);


            menu_add_item_menu_en_es_ca(array_menu_common,MENU_OPCION_NORMAL,menu_about_acknowledgements,NULL,
                "A~~cknowledgements","Agrade~~cimientos","Re~~coneixements");
			menu_add_item_menu_shortcut(array_menu_common,'c');
             menu_add_item_menu_se_cerrara(array_menu_common);
            menu_add_item_menu_genera_ventana(array_menu_common);

            menu_add_item_menu_en_es_ca(array_menu_common,MENU_OPCION_NORMAL,menu_about_donors,NULL,
                "~~Donors","~~Donantes","~~Donants");
			menu_add_item_menu_shortcut(array_menu_common,'d');
             menu_add_item_menu_se_cerrara(array_menu_common);
            menu_add_item_menu_genera_ventana(array_menu_common);

			menu_add_item_menu(array_menu_common,"Donate",MENU_OPCION_NORMAL,menu_about_donate,NULL);
            menu_add_item_menu_spanish(array_menu_common,"Donar");
            menu_add_item_menu_se_cerrara(array_menu_common);
            menu_add_item_menu_genera_ventana(array_menu_common);

			menu_add_item_menu(array_menu_common,"FA~~Q",MENU_OPCION_NORMAL,menu_about_faq,NULL);
			menu_add_item_menu_shortcut(array_menu_common,'q');
            menu_add_item_menu_se_cerrara(array_menu_common);
            menu_add_item_menu_genera_ventana(array_menu_common);

			menu_add_item_menu(array_menu_common,"Cha~~ngelog",MENU_OPCION_NORMAL,menu_about_changelog,NULL);
			menu_add_item_menu_shortcut(array_menu_common,'n');
            menu_add_item_menu_se_cerrara(array_menu_common);
            menu_add_item_menu_genera_ventana(array_menu_common);

			menu_add_item_menu_en_es_ca(array_menu_common,MENU_OPCION_NORMAL,menu_about_alternate_roms,NULL,
                "Alternate RO~~MS","RO~~MS alternativas","RO~~MS alternatives");
			menu_add_item_menu_shortcut(array_menu_common,'m');
            menu_add_item_menu_se_cerrara(array_menu_common);
            menu_add_item_menu_genera_ventana(array_menu_common);

			menu_add_item_menu_en_es_ca(array_menu_common,MENU_OPCION_NORMAL,menu_about_included_tapes,NULL,
                "Included ~~tapes","Cin~~tas incluidas","Cin~~tes incloses");
			menu_add_item_menu_shortcut(array_menu_common,'t');
            menu_add_item_menu_se_cerrara(array_menu_common);
            menu_add_item_menu_genera_ventana(array_menu_common);

			menu_add_item_menu(array_menu_common,"Insta~~ll",MENU_OPCION_NORMAL,menu_about_install,NULL);
            menu_add_item_menu_spanish(array_menu_common,"Insta~~lar");
			menu_add_item_menu_shortcut(array_menu_common,'l');
            menu_add_item_menu_se_cerrara(array_menu_common);
            menu_add_item_menu_genera_ventana(array_menu_common);

			menu_add_item_menu_en_es_ca(array_menu_common,MENU_OPCION_NORMAL,menu_about_installwindows,NULL,
                "Install on ~~Windows","Instalar en ~~Windows","Instalar a ~~Windows");
			menu_add_item_menu_shortcut(array_menu_common,'w');
            menu_add_item_menu_se_cerrara(array_menu_common);
            menu_add_item_menu_genera_ventana(array_menu_common);

            menu_add_item_menu_en_es_ca(array_menu_common,MENU_OPCION_NORMAL,menu_about_compile_info,NULL,
                "C~~ompile info","Información de c~~ompilación","Informació de c~~ompilació");
			menu_add_item_menu_shortcut(array_menu_common,'o');
            menu_add_item_menu_se_cerrara(array_menu_common);
            menu_add_item_menu_genera_ventana(array_menu_common);

            menu_add_item_menu_en_es_ca(array_menu_common,MENU_OPCION_NORMAL,menu_about_statistics,NULL,
                "~~Statistics","E~~stadísticas","E~~stadístiques");
			menu_add_item_menu_shortcut(array_menu_common,'s');
            menu_add_item_menu_se_cerrara(array_menu_common);
            menu_add_item_menu_genera_ventana(array_menu_common);

            menu_add_item_menu_en_es_ca(array_menu_common,MENU_OPCION_NORMAL,menu_about_core_statistics,NULL,
                "Core Statistics","Estadísticas del núcleo","Estadístiques del nucli");
			//menu_add_item_menu_shortcut(array_menu_common,'r');
            menu_add_item_menu_se_cerrara(array_menu_common);
            menu_add_item_menu_genera_ventana(array_menu_common);

			menu_add_item_menu_en_es_ca(array_menu_common,MENU_OPCION_NORMAL,menu_about_running_info,NULL,
                "R~~unning info","Información de ejec~~ución","Informació d'exec~~ució");
			menu_add_item_menu_shortcut(array_menu_common,'u');
            menu_add_item_menu_se_cerrara(array_menu_common);
            menu_add_item_menu_genera_ventana(array_menu_common);

            menu_add_item_menu_separator(array_menu_common);

			menu_add_item_menu_en_es_ca(array_menu_common,MENU_OPCION_NORMAL,menu_licenses,NULL,
                "Lic~~enses","Lic~~encias","Llic~~ències");
			menu_add_item_menu_shortcut(array_menu_common,'e');
            menu_add_item_menu_tiene_submenu(array_menu_common);

            menu_add_item_menu(array_menu_common,"In Memoriam",MENU_OPCION_NORMAL,menu_in_memoriam,NULL);
            //menu_add_item_menu_se_cerrara(array_menu_common);
            menu_add_item_menu_tiene_submenu(array_menu_common);


            menu_add_item_menu_separator(array_menu_common);

            menu_add_ESC_item(array_menu_common);

            menu_add_item_menu_index_full_path(array_menu_common,"Main Menu-> Help","Menú Principal-> Help","Menú Principal-> Help");

            retorno_menu=menu_dibuja_menu(&about_opcion_seleccionada,&item_seleccionado,array_menu_common,
                "Help Menu","Menú Help","Menú Help" );



            if ((item_seleccionado.tipo_opcion&MENU_OPCION_ESC)==0 && retorno_menu>=0) {
            	//llamamos por valor de funcion
                if (item_seleccionado.menu_funcion!=NULL) {
                	//printf ("actuamos por funcion\n");
                    item_seleccionado.menu_funcion(item_seleccionado.valor_opcion);

                }
            }

        } while ( (item_seleccionado.tipo_opcion&MENU_OPCION_ESC)==0 && retorno_menu!=MENU_RETORNO_ESC && !salir_todos_menus);

}




void menu_input_file_keyboard_insert(MENU_ITEM_PARAMETERS)
{

        if (input_file_keyboard_inserted.v==0) {
                input_file_keyboard_init();
        }

        else if (input_file_keyboard_inserted.v==1) {
                input_file_keyboard_close();
        }

}



int menu_input_file_keyboard_cond(void)
{
        if (input_file_keyboard_name!=NULL) return 1;
        else return 0;
}

void menu_input_file_keyboard_delay(MENU_ITEM_PARAMETERS)
{

    //Final valor =>  input_file_keyboard_delay=((Valor en ms)*50)/1000
    int valor_ms=util_get_input_file_keyboard_ms();

    menu_ventana_scanf_numero_enhanced("Key length",&valor_ms,5,+20,20,2000,1);


    input_file_keyboard_delay=(valor_ms*50)/1000;

    //Por si acaso controlar los limites despues de hacer este calculo
    //1->20 ms. 100->2000 ms
    if (input_file_keyboard_delay<1 || input_file_keyboard_delay>100) input_file_keyboard_delay=1;


}

void menu_input_file_keyboard(MENU_ITEM_PARAMETERS)
{

        input_file_keyboard_inserted.v=0;
		input_file_keyboard_playing.v=0;


        char *filtros[2];

        filtros[0]="txt";
        filtros[1]=0;


        if (menu_filesel("Select Spool File",filtros,input_file_keyboard_name_buffer)==1) {
		input_file_keyboard_name=input_file_keyboard_name_buffer;

        }

        else {
		input_file_keyboard_name=NULL;
		eject_input_file_keyboard();
        }
}


void menu_input_file_keyboard_send_pause(MENU_ITEM_PARAMETERS)
{
	input_file_keyboard_send_pause.v ^=1;
}


void menu_input_file_keyboard_turbo(MENU_ITEM_PARAMETERS)
{

	if (input_file_keyboard_turbo.v) {
		reset_peek_byte_function_spoolturbo();
		input_file_keyboard_turbo.v=0;
	}

	else {
		set_peek_byte_function_spoolturbo();
		input_file_keyboard_turbo.v=1;
	}
}


int menu_input_file_keyboard_turbo_cond(void)
{
	if (MACHINE_IS_SPECTRUM) return 1;
    //if (MACHINE_IS_ACE) return 1;
	return 0;
}


void menu_input_file_keyboard_play(MENU_ITEM_PARAMETERS)
{
	input_file_keyboard_playing.v ^=1;
}

void menu_debug_input_file_keyboard(MENU_ITEM_PARAMETERS)
{
    menu_item *array_menu_input_file_keyboard;
    menu_item item_seleccionado;
    int retorno_menu;

    do {

        char string_input_file_keyboard_shown[16];
        menu_tape_settings_trunc_name(input_file_keyboard_name,string_input_file_keyboard_shown,16);
        menu_add_item_menu_inicial_format(&array_menu_input_file_keyboard,MENU_OPCION_NORMAL,menu_input_file_keyboard,NULL,"Spool file [%s]",string_input_file_keyboard_shown);


        menu_add_item_menu_format(array_menu_input_file_keyboard,MENU_OPCION_NORMAL,menu_input_file_keyboard_insert,menu_input_file_keyboard_cond,"[%c] Spool file inserted",(input_file_keyboard_inserted.v ? 'X' : ' ' ));
        if (input_file_keyboard_inserted.v) {

            menu_add_item_menu_format(array_menu_input_file_keyboard,MENU_OPCION_NORMAL,menu_input_file_keyboard_play,NULL,"[%c] Spool file playing",(input_file_keyboard_playing.v ? 'X' : ' ' ));

            //en tbblue no va bien la opcion de turbo
            if (!MACHINE_IS_TBBLUE) {
                menu_add_item_menu_format(array_menu_input_file_keyboard,MENU_OPCION_NORMAL,menu_input_file_keyboard_turbo,menu_input_file_keyboard_turbo_cond,"[%c] Turbo mode",(input_file_keyboard_turbo.v ? 'X' : ' ') );
                menu_add_item_menu_tooltip(array_menu_input_file_keyboard,"Allow turbo mode on Spectrum models");
                menu_add_item_menu_ayuda(array_menu_input_file_keyboard,"Allow turbo mode on Spectrum models. It traps calls to ROM when keyboard is read.\n"
                                    "Works well with Spectrum Basic but also with Text Adventures made with Daad, Paws and GAC");
            }


            if (input_file_keyboard_turbo.v==0) {

                            menu_add_item_menu_format(array_menu_input_file_keyboard,MENU_OPCION_NORMAL,menu_input_file_keyboard_delay,NULL,"[%d ms] Key length",util_get_input_file_keyboard_ms() );
                            menu_add_item_menu_tooltip(array_menu_input_file_keyboard,"Length of every key pressed");
                            menu_add_item_menu_ayuda(array_menu_input_file_keyboard,"I recommend 100 ms for entering lines on Spectrum BASIC. I also suggest to send some manual delays, using unhandled character, like \\, to assure entering lines is correct ");

                            menu_add_item_menu_format(array_menu_input_file_keyboard,MENU_OPCION_NORMAL,menu_input_file_keyboard_send_pause,NULL,"[%c] Delay after every key",(input_file_keyboard_send_pause.v==1 ? 'X' : ' ') );
                            menu_add_item_menu_tooltip(array_menu_input_file_keyboard,"Send or not a delay of the same duration after every key");
                            menu_add_item_menu_ayuda(array_menu_input_file_keyboard,"I recommend enabling this for entering lines on Spectrum BASIC");

            }
        }


        menu_add_item_menu(array_menu_input_file_keyboard,"",MENU_OPCION_SEPARADOR,NULL,NULL);

        menu_add_ESC_item(array_menu_input_file_keyboard);

        retorno_menu=menu_dibuja_menu(&input_file_keyboard_opcion_seleccionada,&item_seleccionado,array_menu_input_file_keyboard,
            "Input File Spooling Menu" ,"Menú Input File Spooling","Menú Input File Spooling");


        if ((item_seleccionado.tipo_opcion&MENU_OPCION_ESC)==0 && retorno_menu>=0) {
                //llamamos por valor de funcion
                if (item_seleccionado.menu_funcion!=NULL) {
                        //printf ("actuamos por funcion\n");
                        item_seleccionado.menu_funcion(item_seleccionado.valor_opcion);

                }
        }

    } while ( (item_seleccionado.tipo_opcion&MENU_OPCION_ESC)==0 && retorno_menu!=MENU_RETORNO_ESC && !salir_todos_menus);
}




void menu_audio_record_input_enable(MENU_ITEM_PARAMETERS)
{
    if (!audio_is_recording_input) {
        menu_first_aid("ext_audio_source");
        audiodriver_start_record_input();
    }
    else {
        audio_stop_record_input();
    }
}

#ifdef COMPILE_SDL
#ifdef COMPILE_SDL2
void menu_audio_record_sdl_next_device_capture(MENU_ITEM_PARAMETERS)
{
    audiosdl_next_device_capture();
}
#endif
#endif




//Valores previos para vu meters dac
int menu_audio_specnext_dac_previos_dac[4];

//Valores previos para vu meters volumenes
//int menu_audio_specnext_dac_previos_volumes[4];

//Valores previos para vu meters left right
int menu_audio_specnext_dac_previo_left;
int menu_audio_specnext_dac_previo_right;


int menu_audio_specnext_dac_contador_segundo_anterior;


zxvision_window *menu_specnext_audio_dac_window;


void menu_specnext_audio_dac_overlay(void)
{

    menu_speech_set_tecla_pulsada(); //Si no, envia continuamente todo ese texto a speech

    //si ventana minimizada, no ejecutar todo el codigo de overlay
    if (menu_specnext_audio_dac_window->is_minimized) return;


    int decaer_volumenes=0;

    //esto hara ejecutar esto 2 veces por segundo
    if ( ((contador_segundo%500) == 0 && menu_audio_specnext_dac_contador_segundo_anterior!=contador_segundo) || menu_multitarea==0) {

        menu_audio_specnext_dac_contador_segundo_anterior=contador_segundo;

        decaer_volumenes=1;
    }

    zxvision_window *ventana=menu_specnext_audio_dac_window;


    int linea=0;
    char buffer_linea[73];


    int i;

    //Escalar el maximo del vu-meter al ancho de la ventana
    //13 es lo que ocupa el texto de "DAC %c: %02XH " y los dos mas de margen izquierda y derecha
    int max_ancho_volumenes=ventana->visible_width-13;

    if (max_ancho_volumenes>40) max_ancho_volumenes=40;
    if (max_ancho_volumenes<10) max_ancho_volumenes=10;

    int divisor=128/max_ancho_volumenes;



    //Controlar limites, dado que las variables entran sin inicializar
    for (i=0;i<4;i++) {
        if (menu_audio_specnext_dac_previos_dac[i]>max_ancho_volumenes) menu_audio_specnext_dac_previos_dac[i]=max_ancho_volumenes;
    }


    if (menu_audio_specnext_dac_previo_left>max_ancho_volumenes) menu_audio_specnext_dac_previo_left=max_ancho_volumenes;
    if (menu_audio_specnext_dac_previo_right>max_ancho_volumenes) menu_audio_specnext_dac_previo_right=max_ancho_volumenes;


    int nivel_actual;
    char buf_nivel[49];
    char letra_dac;

    for (i=0;i<4;i++) {
        //VU meters para DAC, para volumes

        z80_byte valor_dac;

        //Valor unsigned con 0 en 128
        switch(i) {
            case 0:
                valor_dac=tbblue_dac_a;
                letra_dac='A';
            break;

            case 1:
                valor_dac=tbblue_dac_b;
                letra_dac='B';
            break;

            case 2:
                valor_dac=tbblue_dac_c;
                letra_dac='C';
            break;

            default:
                valor_dac=tbblue_dac_d;
                letra_dac='D';
            break;

        }

        nivel_actual=valor_dac;

        nivel_actual=nivel_actual-128;

        //Valor absoluto
        if (nivel_actual<0) nivel_actual=-nivel_actual;

        //Y pasar de escala 0..128 a escala 0..max_ancho_volumenes
        nivel_actual /=divisor;

        if (nivel_actual>max_ancho_volumenes) nivel_actual=max_ancho_volumenes;

        menu_audio_specnext_dac_previos_dac[i]=menu_decae_ajusta_valor_volumen(menu_audio_specnext_dac_previos_dac[i],nivel_actual);


        //char buf_nivel[33];


        menu_string_volumen_n_longitud(buf_nivel,nivel_actual,menu_audio_specnext_dac_previos_dac[i],max_ancho_volumenes);
        sprintf (buffer_linea,"DAC %c: %02XH %s",letra_dac,valor_dac,buf_nivel);
        zxvision_print_string_defaults_fillspc(ventana,1,linea++,buffer_linea);

        if (decaer_volumenes) {
            //printf("Decaer volumen %d\n",i);
            //printf("decae actual %d\n",menu_audio_specnext_dac_previos_dac[i]);
            menu_audio_specnext_dac_previos_dac[i]=menu_decae_dec_valor_volumen(menu_audio_specnext_dac_previos_dac[i],nivel_actual);
        }
    }

    int dac_a=tbblue_dac_a-128;
    int dac_b=tbblue_dac_b-128;
    int dac_c=tbblue_dac_c-128;
    int dac_d=tbblue_dac_d-128;


    //Sumar canales izquierdo y derecho
    int dac_izquierdo=(dac_a+dac_b)/2;
    int dac_derecho=(dac_c+dac_d)/2;

    //VU meters para Left


    //Valor unsigned con 0 en 128
    nivel_actual=dac_izquierdo;


    //Valor absoluto
    if (nivel_actual<0) nivel_actual=-nivel_actual;

    //Y pasar de escala 0..128 a escala 0..max_ancho_volumenes
    nivel_actual /=divisor;


    if (nivel_actual>max_ancho_volumenes) nivel_actual=max_ancho_volumenes;

    menu_audio_specnext_dac_previo_left=menu_decae_ajusta_valor_volumen(menu_audio_specnext_dac_previo_left,nivel_actual);



    menu_string_volumen_n_longitud(buf_nivel,nivel_actual,menu_audio_specnext_dac_previo_left,max_ancho_volumenes);
    sprintf (buffer_linea,"Left Out:  %s",buf_nivel);
    zxvision_print_string_defaults_fillspc(ventana,1,linea++,buffer_linea);

    if (decaer_volumenes) {
        menu_audio_specnext_dac_previo_left=menu_decae_dec_valor_volumen(menu_audio_specnext_dac_previo_left,nivel_actual);
    }

    //VU meters para Right


    //Valor unsigned con 0 en 128
    nivel_actual=dac_derecho;


    //Valor absoluto
    if (nivel_actual<0) nivel_actual=-nivel_actual;

    //Y pasar de escala 0..128 a escala 0..max_ancho_volumenes
    nivel_actual /=divisor;


    if (nivel_actual>max_ancho_volumenes) nivel_actual=max_ancho_volumenes;

    menu_audio_specnext_dac_previo_right=menu_decae_ajusta_valor_volumen(menu_audio_specnext_dac_previo_right,nivel_actual);



    menu_string_volumen_n_longitud(buf_nivel,nivel_actual,menu_audio_specnext_dac_previo_right,max_ancho_volumenes);
    sprintf (buffer_linea,"Right Out: %s",buf_nivel);
    zxvision_print_string_defaults_fillspc(ventana,1,linea++,buffer_linea);

    if (decaer_volumenes) {
        menu_audio_specnext_dac_previo_right=menu_decae_dec_valor_volumen(menu_audio_specnext_dac_previo_right,nivel_actual);
    }



    //Mostrar contenido
    zxvision_draw_window_contents(menu_specnext_audio_dac_window);

}








//Almacenar la estructura de ventana aqui para que se pueda referenciar desde otros sitios
zxvision_window zxvision_window_specnext_audio_dac;


void menu_specnext_audio_dac(MENU_ITEM_PARAMETERS)
{
	menu_espera_no_tecla();

    if (!menu_multitarea) {
        menu_warn_message("This window needs multitask enabled");
        return;
    }

    zxvision_window *ventana;
    ventana=&zxvision_window_specnext_audio_dac;

	//IMPORTANTE! no crear ventana si ya existe. Esto hay que hacerlo en todas las ventanas que permiten background.
	//si no se hiciera, se crearia la misma ventana, y en la lista de ventanas activas , al redibujarse,
	//la primera ventana repetida apuntaria a la segunda, que es el mismo puntero, y redibujaria la misma, y se quedaria en bucle colgado
	//zxvision_delete_window_if_exists(ventana);

    //Crear ventana si no existe
    if (!zxvision_if_window_already_exists(ventana)) {
        int xventana,yventana,ancho_ventana,alto_ventana,is_minimized,is_maximized,ancho_antes_minimize,alto_antes_minimize;

        if (!util_find_window_geometry("specnextaudiodac",&xventana,&yventana,&ancho_ventana,&alto_ventana,&is_minimized,&is_maximized,&ancho_antes_minimize,&alto_antes_minimize)) {
            ancho_ventana=44;
            alto_ventana=8;

            xventana=menu_center_x()-ancho_ventana/2;
            yventana=menu_center_y()-alto_ventana/2;
        }


        zxvision_new_window_gn_cim(ventana,xventana,yventana,ancho_ventana,alto_ventana,ancho_ventana-1,alto_ventana-2,"Next DAC",
            "specnextaudiodac",is_minimized,is_maximized,ancho_antes_minimize,alto_antes_minimize);

        ventana->can_be_backgrounded=1;

    }

    //Si ya existe, activar esta ventana
    else {
        zxvision_activate_this_window(ventana);
    }

	zxvision_draw_window(ventana);

	z80_byte tecla;


	int salir=0;


    menu_specnext_audio_dac_window=ventana; //Decimos que el overlay lo hace sobre la ventana que tenemos aqui


    //cambio overlay
    zxvision_set_window_overlay(ventana,menu_specnext_audio_dac_overlay);


    //Toda ventana que este listada en zxvision_known_window_names_array debe permitir poder salir desde aqui
    //Se sale despues de haber inicializado overlay y de cualquier otra variable que necesite el overlay
    if (zxvision_currently_restoring_windows_on_start) {
            //printf ("Saliendo de ventana ya que la estamos restaurando en startup\n");
            return;
    }

    do {


		tecla=zxvision_common_getkey_refresh();


        switch (tecla) {

            case 11:
                //arriba
                //blablabla
            break;



            //Salir con ESC
            case 2:
                salir=1;
            break;

            //O tecla background
            case 3:
                salir=1;
            break;
        }


    } while (salir==0);


	util_add_window_geometry_compact(ventana);

	if (tecla==3) {
		zxvision_message_put_window_background();
	}

	else {

		zxvision_destroy_window(ventana);
	}


}

void menu_external_audio_source_to_disk_enable(MENU_ITEM_PARAMETERS)
{
    if (!audiorecord_input_write_to_disk_enabled) {
        audiorecord_input_write_to_disk_enable_capture();
    }
    else {
        audiorecord_input_write_to_disk_disable_capture();
    }
}

int menu_external_audio_source_to_disk_enable_cond(void)
{
    if (audiorecord_input_write_to_disk_file_name[0]) return 1;
    else return 0;
}

void menu_external_audio_source_to_disk_select_file(MENU_ITEM_PARAMETERS)
{
    //disable if enabled
    if (audiorecord_input_write_to_disk_enabled) {
        audiorecord_input_write_to_disk_disable_capture();
    }

    //select file

#ifdef USE_SNDFILE
    char *filtros[]={"wav","raw","rwa",0};
#else
	char *filtros[]={"raw","rwa",0};
#endif

	//guardamos directorio actual
	char directorio_actual[PATH_MAX];
	getcwd(directorio_actual,PATH_MAX);

	//Obtenemos directorio de parametro

	if (audiorecord_input_write_to_disk_file_name[0]) {
        char directorio[PATH_MAX];
        util_get_dir(audiorecord_input_write_to_disk_file_name,directorio);
        //printf ("strlen directorio: %d directorio: %s\n",strlen(directorio),directorio);

		//cambiamos a ese directorio, siempre que no sea nulo
		if (directorio[0]!=0) {
			debug_printf (VERBOSE_INFO,"Changing to last directory: %s",directorio);
			zvfs_chdir(directorio);
		}
	}


    int ret;

    ret=menu_filesel_save("Select Output File",filtros,audiorecord_input_write_to_disk_file_name);
    //volvemos a directorio inicial
    zvfs_chdir(directorio_actual);


	if (ret==1) {

		//Ver si archivo existe y preguntar

		if (si_existe_archivo(audiorecord_input_write_to_disk_file_name)) {

            if (menu_confirm_yesno_texto("File exists","Overwrite?")==0) {
                //quitamos archivo de la config
                audiorecord_input_write_to_disk_file_name[0]=0;
                return;
            }

		}

        //Cambiar samplerate si RWA
        if (!util_compare_file_extension(audiorecord_input_write_to_disk_file_name,"rwa")) {
            audiorecord_input_write_to_disk_output_freq=15600;
        }

        //Y si es RAW, consideramos que es misma frecuencia a la que se captura
        if (!util_compare_file_extension(audiorecord_input_write_to_disk_file_name,"raw")) {
            audiorecord_input_write_to_disk_output_freq=AUDIO_RECORD_FREQUENCY;
        }

        //and enable
        audiorecord_input_write_to_disk_enable_capture();
	}


}

void menu_external_audio_source_to_disk_samplerate(MENU_ITEM_PARAMETERS)
{
    menu_ventana_scanf_numero_enhanced("Samplerate Hz",&audiorecord_input_write_to_disk_output_freq,7,+100,1,999999,0);
}

int menu_external_audio_source_to_disk_samplerate_cond(void)
{
    if (audiorecord_input_write_to_disk_enabled) return 0;
    else return 1;
}


void menu_external_audio_source_to_disk(MENU_ITEM_PARAMETERS)
{
    menu_item *array_menu_common;
    menu_item item_seleccionado;
    int retorno_menu;


    do {


        menu_add_item_menu_en_es_ca_inicial(&array_menu_common,MENU_OPCION_NORMAL,
            menu_external_audio_source_to_disk_enable,menu_external_audio_source_to_disk_enable_cond,
            "Enabled","Activado","Activat");
        menu_add_item_menu_prefijo_format(array_menu_common,"[%c] ",(audiorecord_input_write_to_disk_enabled ? 'X' : ' ') );

        //No pongo tooltip, asi el usuario puede dejar el cursor aqui mientras graba, y luego desactivar al final,
        //sin que le moleste el tooltip
		//menu_add_item_menu_tooltip(array_menu_common,"Enable recording to file");
		//menu_add_item_menu_ayuda(array_menu_common,"Enable recording to file");


        char string_file_shown[20];
		menu_tape_settings_trunc_name(audiorecord_input_write_to_disk_file_name,string_file_shown,20);

		menu_add_item_menu_en_es_ca(array_menu_common,MENU_OPCION_NORMAL,menu_external_audio_source_to_disk_select_file,NULL,
            "Output File","Archivo Salida","Arxiu Sortida");
        menu_add_item_menu_sufijo_format(array_menu_common," [%s]",string_file_shown);
        menu_add_item_menu_prefijo(array_menu_common,"    ");


		menu_add_item_menu_en_es_ca(array_menu_common,MENU_OPCION_NORMAL,
            menu_external_audio_source_to_disk_samplerate,menu_external_audio_source_to_disk_samplerate_cond,
            "Sample Rate","Frecuencia Sampleo","Frequencia Sampleig");
        menu_add_item_menu_sufijo_format(array_menu_common," [%d] Hz",audiorecord_input_write_to_disk_output_freq);
        menu_add_item_menu_prefijo(array_menu_common,"    ");
        menu_add_item_menu_tooltip(array_menu_common,"Output file samplerate");

        //OJO no salirse de este tamanyo con texto largo
        char texto_ayuda_samplerate[200];
        sprintf(texto_ayuda_samplerate,"Audio is captured at %d Hz so any output samplerate bigger than this will be resampled "
            "but won't have more definition",AUDIO_RECORD_FREQUENCY);
        menu_add_item_menu_ayuda(array_menu_common,texto_ayuda_samplerate);

        if (audiorecord_input_write_to_disk_enabled) {
            menu_add_item_menu_en_es_ca(array_menu_common,MENU_OPCION_SEPARADOR,NULL,NULL,
                "Recorded time","Tiempo de Grabación","Temps de Gravació");
            menu_add_item_menu_prefijo(array_menu_common,"    ");

            int minutos,segundos;
            audiorecord_input_write_to_disk_time_size(&minutos,&segundos);

            menu_add_item_menu_sufijo_format(array_menu_common," %02d:%02d",minutos,segundos);


            long long tamanyo=audiorecord_input_write_to_disk_total_size();
            menu_add_item_menu_en_es_ca(array_menu_common,MENU_OPCION_SEPARADOR,NULL,NULL,
                "Total Size","Tamaño Total","Tamany Total");
            menu_add_item_menu_prefijo(array_menu_common,"    ");

            //pasar a MB
            tamanyo /= 1024;

            menu_add_item_menu_sufijo_format(array_menu_common," %lld KB",tamanyo);
        }


        menu_add_item_menu_separator(array_menu_common);

        menu_add_ESC_item(array_menu_common);


        //Nota: si no se agrega el nombre del path del indice, se generará uno automáticamente
        menu_add_item_menu_index_full_path(array_menu_common,
            "Main Menu-> Audio-> Capture Audio to File",
            "Menú Principal-> Audio-> Capturar Audio a Archivo",
            "Menú Principal-> Audio-> Capturar Audio a Arxiu");

        retorno_menu=menu_dibuja_menu(&external_audio_source_to_disk_opcion_seleccionada,&item_seleccionado,array_menu_common,
            "Capture Audio to File Menu","Menú Capturar Audio a Archivo","Menú Capturar Audio a Arxiu" );

        if ((item_seleccionado.tipo_opcion&MENU_OPCION_ESC)==0 && retorno_menu>=0) {
            //llamamos por valor de funcion
            if (item_seleccionado.menu_funcion!=NULL) {
                //printf ("actuamos por funcion\n");
                item_seleccionado.menu_funcion(item_seleccionado.valor_opcion);

            }
        }

    } while ( (item_seleccionado.tipo_opcion&MENU_OPCION_ESC)==0 && retorno_menu!=MENU_RETORNO_ESC && !salir_todos_menus);

}



//menu audio
void menu_audio(MENU_ITEM_PARAMETERS)
{
    menu_item *array_menu_audio;
    menu_item item_seleccionado;
    int retorno_menu;

    do {



        menu_add_item_menu_inicial(&array_menu_audio,"Audio Chip ~~Registers",MENU_OPCION_NORMAL,menu_ay_registers,menu_cond_ay_or_sn_chip);
        menu_add_item_menu_spanish_catalan(array_menu_audio,"~~Registros del Chip de Audio","~~Registres del Xip d'Audio");
        menu_add_item_menu_shortcut(array_menu_audio,'r');
        menu_add_item_menu_se_cerrara(array_menu_audio);
        menu_add_item_menu_genera_ventana(array_menu_audio);


        menu_add_item_menu_en_es_ca(array_menu_audio,MENU_OPCION_NORMAL,menu_ay_pianokeyboard,menu_cond_ay_or_sn_chip,
            "Audio Chip P~~iano","P~~iano del Chip de Audio","P~~iano del Xip d'Audio");
        menu_add_item_menu_shortcut(array_menu_audio,'i');
        menu_add_item_menu_tooltip(array_menu_audio,"Shows a piano keyboard with the notes being played on the AY Chip");
        menu_add_item_menu_ayuda(array_menu_audio,"Shows a piano keyboard with the notes being played on the AY Chip");
        menu_add_item_menu_se_cerrara(array_menu_audio);
        menu_add_item_menu_genera_ventana(array_menu_audio);


		if (si_complete_video_driver() ) {
                menu_add_item_menu_en_es_ca(array_menu_audio,MENU_OPCION_NORMAL,menu_ay_partitura,menu_cond_ay_or_sn_chip,
                    "Audio Chip ~~Sheet","Partitura~~s del Chip de Audio","Partiture~~s del Xip d'Audio");
                menu_add_item_menu_shortcut(array_menu_audio,'s');
                menu_add_item_menu_se_cerrara(array_menu_audio);
                menu_add_item_menu_genera_ventana(array_menu_audio);
		}

        menu_add_item_menu_en_es_ca(array_menu_audio,MENU_OPCION_NORMAL,menu_audio_chip_info,menu_cond_ay_or_sn_chip,
            "Audio Chip Info","Info del Chip de Audio","Info del Xip d'Audio");
        menu_add_item_menu_se_cerrara(array_menu_audio);
        menu_add_item_menu_genera_ventana(array_menu_audio);

        if (MACHINE_IS_SPECTRUM && gs_enabled.v) {
            menu_add_item_menu_en_es_ca(array_menu_audio,MENU_OPCION_NORMAL,menu_audio_general_sound,NULL,
                "General Sound Info","General Sound Info","General Sound Info");
            menu_add_item_menu_se_cerrara(array_menu_audio);
            menu_add_item_menu_genera_ventana(array_menu_audio);
        }

        if (MACHINE_IS_TBBLUE) {
            menu_add_item_menu_en_es_ca(array_menu_audio,MENU_OPCION_NORMAL,menu_specnext_audio_dac,NULL,
                "Spectrum Next DAC","Spectrum Next DAC","Spectrum Next DAC");
            menu_add_item_menu_se_cerrara(array_menu_audio);
            menu_add_item_menu_genera_ventana(array_menu_audio);
        }


        menu_add_item_menu_en_es_ca(array_menu_audio,MENU_OPCION_NORMAL,menu_beeper_pianokeyboard,NULL,
            "W~~ave Piano","Pi~~ano de onda","Pi~~ano d'ona");
        menu_add_item_menu_shortcut(array_menu_audio,'a');
        menu_add_item_menu_se_cerrara(array_menu_audio);
        menu_add_item_menu_genera_ventana(array_menu_audio);
        menu_add_item_menu_tooltip(array_menu_audio,"Shows a piano keyboard with the note being played through the output speakers");
        menu_add_item_menu_ayuda(array_menu_audio,"Shows a piano keyboard with the note being played through the output speakers. "
            "In case you don't have AY sound or DAC audio, that note is the one that is played through the beeper. "
            "It can be inaccurate with short notes");




        menu_add_item_menu_en_es_ca(array_menu_audio,MENU_OPCION_NORMAL,menu_audio_new_waveform,NULL,
            "~~Waveform","Forma de onda","Forma d'ona");
        menu_add_item_menu_tooltip(array_menu_audio,"Shows the waveform being played through the output speakers");
        menu_add_item_menu_ayuda(array_menu_audio,"Shows the waveform being played through the output speakers");
        menu_add_item_menu_shortcut(array_menu_audio,'w');
        menu_add_item_menu_se_cerrara(array_menu_audio);
        menu_add_item_menu_genera_ventana(array_menu_audio);


        menu_add_item_menu_format(array_menu_audio,MENU_OPCION_NORMAL,menu_audio_new_ayplayer,NULL,"AY ~~Player");
        menu_add_item_menu_tooltip(array_menu_audio,"Opens the .ay file player menu");
        menu_add_item_menu_ayuda(array_menu_audio,"Opens the .ay file player menu");
        menu_add_item_menu_shortcut(array_menu_audio,'p');
        menu_add_item_menu_se_cerrara(array_menu_audio);
        menu_add_item_menu_genera_ventana(array_menu_audio);






        if (audio_can_record_input() ) {
            menu_add_item_menu_separator(array_menu_audio);

            menu_add_item_menu_en_es_ca(array_menu_audio,MENU_OPCION_NORMAL,NULL,NULL,
                "--External Audio Source--","--Fuente de Sonido Externa--","--Font de So Externa--");

            menu_add_item_menu_en_es_ca(array_menu_audio,MENU_OPCION_NORMAL,menu_audio_record_input_enable,NULL,
                "Enable External Audio Source","Activar Fuente de Sonido Externa","Activar Font de So Externa");
            menu_add_item_menu_prefijo_format(array_menu_audio,"[%c] ", (audio_is_recording_input ? 'X' : ' '));
            menu_add_item_menu_tooltip(array_menu_audio,"Allows you to load audio from external audio source, like tape player, "
                "a mp3 player or your phone");
            menu_add_item_menu_ayuda(array_menu_audio,"Allows you to load audio from external audio source, like tape player, "
                "a mp3 player or your phone");

            if (audio_is_recording_input) {

                menu_add_item_menu_en_es_ca(array_menu_audio,MENU_OPCION_NORMAL,menu_external_audio_source_to_disk,NULL,
                    "Capture Audio to File","Capturar Audio a Archivo","Capturar Audio a Arxiu");
                menu_add_item_menu_tooltip(array_menu_audio,"Capture Audio and store to audio file");
                menu_add_item_menu_ayuda(array_menu_audio,"Capture Audio and store to audio file");
                menu_add_item_menu_shortcut(array_menu_audio,'m');
                menu_add_item_menu_tiene_submenu(array_menu_audio);


#ifdef COMPILE_SDL
#ifdef COMPILE_SDL2
                if (!strcmp(audio_new_driver_name,"sdl")) {
                    char buffer_destino[SDL2_MAX_DEVICE_NAME_LENGTH+1];
                    audiosdl_get_device_name(buffer_destino);
                    menu_add_item_menu_en_es_ca(array_menu_audio,MENU_OPCION_NORMAL,menu_audio_record_sdl_next_device_capture,NULL,
                        "Selected card","Tarjeta seleccionada","Targeta seleccionada");
                    menu_add_item_menu_format(array_menu_audio,MENU_OPCION_SEPARADOR,NULL,NULL," %s",buffer_destino);
                    menu_add_item_menu_separator(array_menu_audio);
                }
#endif
#endif


                menu_add_item_menu_en_es_ca(array_menu_audio,MENU_OPCION_NORMAL,menu_realtape_record_input,NULL,
                    "External Audio Source Window","Ventana fuente de sonido externa","Finestra font de so externa");
                menu_add_item_menu_se_cerrara(array_menu_audio);
                menu_add_item_menu_genera_ventana(array_menu_audio);

                menu_add_item_menu_en_es_ca(array_menu_audio,MENU_OPCION_NORMAL,menu_input_spectrum_analyzer,NULL,
                    "Spectrum Analyzer","Analizador de Espectro","Anàlisis d'espectre");
                menu_add_item_menu_se_cerrara(array_menu_audio);
                menu_add_item_menu_genera_ventana(array_menu_audio);

            }
        }


        menu_add_item_menu(array_menu_audio,"",MENU_OPCION_SEPARADOR,NULL,NULL);

        if (MACHINE_IS_QL) {
            menu_add_item_menu_format(array_menu_audio,MENU_OPCION_NORMAL,menu_i8049_mixer,menu_cond_i8049_chip,"i8049 Mi~~xer");
            menu_add_item_menu_shortcut(array_menu_audio,'x');
        }
        else {
            menu_add_item_menu_format(array_menu_audio,MENU_OPCION_NORMAL,menu_ay_mixer,menu_cond_ay_chip,"AY Mi~~xer");
            menu_add_item_menu_shortcut(array_menu_audio,'x');
        }
        menu_add_item_menu_tiene_submenu(array_menu_audio);



        menu_add_item_menu_en_es_ca(array_menu_audio,MENU_OPCION_NORMAL,menu_record_mid,menu_cond_ay_or_sn_chip,
            "Audio Chip to .~~mid","Audio Chip a .~~mid","Audio Xip a .~~mid");
        menu_add_item_menu_tooltip(array_menu_audio,"Saves music from the AY Chip to a .mid file");
        menu_add_item_menu_ayuda(array_menu_audio,"Saves music from the AY Chip to a .mid file");
        menu_add_item_menu_shortcut(array_menu_audio,'m');
        menu_add_item_menu_tiene_submenu(array_menu_audio);


        menu_add_item_menu(array_menu_audio,"",MENU_OPCION_SEPARADOR,NULL,NULL);


		menu_add_ESC_item(array_menu_audio);

        //La llamada a menu_add_item_menu_index_full_path lo agrega al principio del array, por tanto lo podemos llamar desde cualquier
        //punto de la creacion del menu. Lo llamo desde aqui porque queda esteticamente mas bonito
        menu_add_item_menu_index_full_path(array_menu_audio,"Main Menu-> Audio","Menú Principal-> Audio","Menú Principal-> Audio");

        retorno_menu=menu_dibuja_menu(&audio_opcion_seleccionada,&item_seleccionado,array_menu_audio,"Audio Menu","Menú Audio","Menú Audio" );



		if ((item_seleccionado.tipo_opcion&MENU_OPCION_ESC)==0 && retorno_menu>=0) {
            //llamamos por valor de funcion
            if (item_seleccionado.menu_funcion!=NULL) {
                //printf ("actuamos por funcion\n");
                item_seleccionado.menu_funcion(item_seleccionado.valor_opcion);

            }
		}

	} while ( (item_seleccionado.tipo_opcion&MENU_OPCION_ESC)==0 && retorno_menu!=MENU_RETORNO_ESC && !salir_todos_menus);
}


void menu_debug_reset(MENU_ITEM_PARAMETERS)
{
	if (menu_confirm_yesno("Reset CPU")==1) {
		reset_cpu();

	}
    //Y salimos de todos los menus
    salir_todos_menus=1;

}




void menu_debug_prism_failsafe(MENU_ITEM_PARAMETERS)
{
	if (menu_confirm_yesno("Reset to failsafe?")==1) {
                reset_cpu();

		prism_failsafe_mode.v=1;
		//Para aplicar cambio de pagina rom
		prism_set_memory_pages();


	}
    //Y salimos de todos los menus
    salir_todos_menus=1;
}

void menu_debug_hard_reset(MENU_ITEM_PARAMETERS)
{
    if (menu_confirm_yesno("Hard Reset CPU")==1) {
            hard_reset_cpu();
    }
    //Y salimos de todos los menus
    salir_todos_menus=1;

}


void menu_debug_nmi(MENU_ITEM_PARAMETERS)
{
    if (menu_confirm_yesno("Generate NMI")==1) {

        generate_nmi();


    }

    //Y salimos de todos los menus
    salir_todos_menus=1;

}

void menu_debug_nmi_multiface_tbblue(MENU_ITEM_PARAMETERS)
{
    if (menu_confirm_yesno("Generate Multiface NMI")==1) {

		generate_nmi_multiface_tbblue();


    }

    //Y salimos de todos los menus
    salir_todos_menus=1;

}

void menu_debug_special_nmi(MENU_ITEM_PARAMETERS)
{
        if (menu_confirm_yesno("Generate Special NMI")==1) {

		char string_nmi[4];
		int valor_nmi;

		sprintf (string_nmi,"0");

		//Pedir valor de NMIEVENT
	        menu_ventana_scanf("NMIEVENT value (0-255)",string_nmi,4);

        	valor_nmi=parse_string_to_number(string_nmi);

	        if (valor_nmi<0 || valor_nmi>255) {
        	        debug_printf (VERBOSE_ERR,"Invalid value %d",valor_nmi);
                	return;
	        }


		generate_nmi();

		//meter BOOTM a 1 (bit 0)
		zxuno_ports[0] |=1;

		zxuno_set_memory_pages();

		//Mapear sram 13
		//zxuno_memory_paged_new(13);
		zxuno_memory_paged_brandnew[3*2]=zxuno_sram_mem_table_new[13];
		zxuno_memory_paged_brandnew[3*2+1]=zxuno_sram_mem_table_new[13]+8192;

		//Valor nmievent
		zxuno_ports[8]=valor_nmi;


        }

    //Y salimos de todos los menus
    salir_todos_menus=1;

}



menu_z80_moto_int load_binary_last_address=0;
menu_z80_moto_int load_binary_last_length=0;
menu_z80_moto_int save_binary_last_address=0;
menu_z80_moto_int save_binary_last_length=1;




void menu_debug_load_binary(MENU_ITEM_PARAMETERS)
{

	char *filtros[2];

	filtros[0]="";
	filtros[1]=0;


	//guardamos directorio actual
	char directorio_actual[PATH_MAX];
	getcwd(directorio_actual,PATH_MAX);

	int ret;

	//Obtenemos ultimo directorio visitado
	if (binary_file_load[0]!=0) {
		char directorio[PATH_MAX];
		util_get_dir(binary_file_load,directorio);
		//printf ("strlen directorio: %d directorio: %s\n",strlen(directorio),directorio);

		//cambiamos a ese directorio, siempre que no sea nulo
		if (directorio[0]!=0) {
				debug_printf (VERBOSE_INFO,"Changing to last directory: %s",directorio);
				zvfs_chdir(directorio);
		}
	}

	ret=menu_filesel("Select File to Load",filtros,binary_file_load);

	//volvemos a directorio inicial
	zvfs_chdir(directorio_actual);

	if (ret==1) {



		menu_debug_change_memory_zone();


		char string_direccion[10];
		sprintf (string_direccion,"%XH",load_binary_last_address);
		menu_ventana_scanf("Address: ",string_direccion,10);
		int valor_leido_direccion=parse_string_to_number(string_direccion);
		load_binary_last_address=valor_leido_direccion;



		char string_longitud[8];
		sprintf (string_longitud,"%d",load_binary_last_length);
		menu_ventana_scanf("Length: 0 - all",string_longitud,8);
		int valor_leido_longitud=parse_string_to_number(string_longitud);
		load_binary_last_length=valor_leido_longitud;

		load_binary_file(binary_file_load,valor_leido_direccion,valor_leido_longitud);

		//Y salimos de todos los menus
		salir_todos_menus=1;


    }



}




/*void menu_debug_view_basic_listing(MENU_ITEM_PARAMETERS)
{

    char *results_buffer=util_malloc_max_texto_generic_message("Can not allocate memory for showing basic");
	debug_view_basic(results_buffer);

    menu_generic_message_format("View Basic","%s",results_buffer);

    free(results_buffer);
}
*/

/*



z80_byte *last_bas_browser_memory;
int last_bas_browser_memory_size=1;

z80_byte menu_file_bas_browser_show_peek(z80_int dir)
{
	return last_bas_browser_memory[dir % last_bas_browser_memory_size]; //con % para no salirnos de la memoria
}
*/


zxvision_window *menu_view_basic_listing_window;

//1 linea final en blanco para separar
#define VIEW_BASIC_HEADER_LINES 5

#define MENU_VIEW_BASIC_LISTING_LINE_SETTINGS 0
#define MENU_VIEW_BASIC_LISTING_LINE_FIND 1
#define MENU_VIEW_BASIC_LISTING_LINE_POINTER 2
#define MENU_VIEW_BASIC_LISTING_LINE_LINE 3

//1024kb al descomponer tokens en texto creo que es mas que suficiente
#define VIEW_BASIC_MAX_BASIC_TEXT (1024*1024)

//Mas que suficiente creo contando lineas que se cortan y van a la siguiente
#define VIEW_BASIC_MAX_LINES_FINAL (9999*10)


//Para poder hacer view basic desde direcciones de memoria
z80_bit menu_view_basic_listing_memory_enabled={0};
int menu_view_basic_listing_memory_pointer=23755;  //0x5cde=23774 primer bloque basic cargado en Cargador Azul
int menu_view_basic_listing_memory_length=10000;

z80_long_int menu_view_basic_listing_last_crc32=0;


//Calcular crc32 del bloque listado por saber si se modifica para volver a renderizar
z80_long_int menu_view_basic_listing_get_crc32(void)
{


    int start_address=menu_view_basic_listing_memory_pointer;
    int length=menu_view_basic_listing_memory_length;

    if (length<1) {
      //escribir_socket(misocket,"ERROR. Length must be >0");
      return 0;
    }


    //Copiar contenido memoria segun memory zone activa a buffer de memoria temporal
    z80_byte *memoria_temporal;
    memoria_temporal=malloc(length);
    if (memoria_temporal==NULL) cpu_panic("Can not allocate memory for crc32 calculation");

    int longitud_copiar=length;
    int i;

    for (i=0;longitud_copiar>0;i++,longitud_copiar--) {
      z80_byte byte_leido=peek_byte_no_time(start_address+i);
      memoria_temporal[i]=byte_leido;
    }



    z80_long_int crc32=util_crc32_calculation(0,memoria_temporal,length);


    free(memoria_temporal);

    return crc32;

}

//Definir parametros basic y ademas establecer variables de inico y final segun si custom pointer
void menu_view_basic_listing_get_basic_parameters(view_basic_parameters *parameters)
{


    debug_view_basic_prepare_parameters(parameters);

    if (menu_view_basic_listing_memory_enabled.v) {
        parameters->dir_inicio_linea=menu_view_basic_listing_memory_pointer;
        parameters->final_basic=menu_view_basic_listing_memory_pointer+menu_view_basic_listing_memory_length;
    }

    else {
        menu_view_basic_listing_memory_pointer=parameters->dir_inicio_linea;
        menu_view_basic_listing_memory_length=parameters->final_basic-parameters->dir_inicio_linea;
    }


}

void menu_view_basic_listing_get_basic(char *results_buffer,int show_current_line)
{


    view_basic_parameters parameters;

    menu_view_basic_listing_get_basic_parameters(&parameters);


	debug_view_basic_from_memory(results_buffer,parameters.dir_inicio_linea,parameters.final_basic,parameters.dir_tokens,
        parameters.inicio_tokens,peek_byte_no_time,parameters.tipo,
        debug_view_basic_show_address.v,show_current_line,debug_view_basic_show_betabasic.v,VIEW_BASIC_MAX_BASIC_TEXT,0);



  	//printf("%s\n",results_buffer);



}

//Punteros a las diferentes lineas
char **menu_view_basic_listing_punteros_lineas=NULL;

//Donde almacenar las lineas ya troceadas
char *menu_view_basic_listing_buffer_lineas=NULL;

int menu_view_basic_listing_total_lineas=0;

void menu_view_basic_listing_print_basic(char *results_buffer,zxvision_window *w)
{
    //trocear
    //Punteros a cada linea de esas
    //Esto es muy maximo, pero podria pasar
    //Un maximo de VIEW_BASIC_MAX_LINES_FINAL lineas

    //char *punteros_lineas[VIEW_BASIC_MAX_LINES_FINAL];
    if (menu_view_basic_listing_punteros_lineas!=NULL) {
        free(menu_view_basic_listing_punteros_lineas);
        menu_view_basic_listing_punteros_lineas=NULL;
    }

    if (menu_view_basic_listing_punteros_lineas==NULL) {
        menu_view_basic_listing_punteros_lineas=util_malloc(VIEW_BASIC_MAX_LINES_FINAL*sizeof(char *),"Can not allocate memory for basic lines");
    }

    //Maximo de linea contando 0 del final
    int max_line_length=w->total_width;

    //maximo permitido en ancho
    int maxima_longitud=max_line_length-1;

    //printf("antes de malloc buffer lineas\n");
    if (menu_view_basic_listing_buffer_lineas!=NULL) {
        //printf("Liberando menu_view_basic_listing_buffer_lineas\n");
        free(menu_view_basic_listing_buffer_lineas);
        menu_view_basic_listing_buffer_lineas=NULL;
    }

    if (menu_view_basic_listing_buffer_lineas==NULL) {
        //printf("Asignando menu_view_basic_listing_buffer_lineas\n");
        menu_view_basic_listing_buffer_lineas=util_malloc(VIEW_BASIC_MAX_LINES_FINAL*max_line_length,"Can not allocate memory for basic lines");
    }


    //Inicializar punteros a lineas
    int i;
    for (i=0;i<VIEW_BASIC_MAX_LINES_FINAL;i++) {
        //printf("puntero linea %d\n",i);
        int offset_linea=i*max_line_length;
        menu_view_basic_listing_punteros_lineas[i]=&menu_view_basic_listing_buffer_lineas[offset_linea];
    }


    menu_view_basic_listing_total_lineas=zxvision_generic_message_aux_justificar_lineas(results_buffer,strlen(results_buffer),maxima_longitud,menu_view_basic_listing_punteros_lineas);

    //Reajustar alto total ventana
    //Darle lineas en blanco al final para que se pueda hacer follow de lineas hacia el final tambien (sino el scroll no bajaria mas)
    int aumentar_tamanyo=w->visible_height-VIEW_BASIC_HEADER_LINES-2-1;
    zxvision_set_total_height(w,VIEW_BASIC_HEADER_LINES+menu_view_basic_listing_total_lineas+aumentar_tamanyo);

    for (i=0;i<menu_view_basic_listing_total_lineas;i++) {
        //printf("linea %d : %s\n",i,punteros_lineas[i]);
        zxvision_print_string_defaults_fillspc(w,1,VIEW_BASIC_HEADER_LINES+i,menu_view_basic_listing_punteros_lineas[i]);
    }

    //limpiar las lineas de margen por debajo
    for (;i<menu_view_basic_listing_total_lineas+aumentar_tamanyo;i++) {
        zxvision_print_string_defaults_fillspc(w,1,VIEW_BASIC_HEADER_LINES+i,"");
    }

}

//Si contenido se ha modificado y hay que hacer dump de nuevo
int menu_view_basic_listing_modified=0;

int menu_view_basic_contador_segundo_anterior=0;

char *menu_view_basic_listing_results_buffer=NULL;

z80_bit menu_view_basic_listing_follow_current_line={0};

int menu_view_basic_listing_follow_line_last=-1;

//Retorna indice de linea en array si encontrada
//-1 si no encontrada
int menu_view_basic_listing_find_line_aux(int linea_buscar)
{

    int i;

    char buffer_linea_buscar[7];
    //Numero y espacio
    sprintf(buffer_linea_buscar,"%5d ",linea_buscar);

    for (i=0;i<menu_view_basic_listing_total_lineas;i++) {
        char *linea=menu_view_basic_listing_punteros_lineas[i];
        //printf("%d : %s\n",i,linea);

        //Numero y espacio
        if (strlen(linea)>=6) {
            if (!strncmp(linea,buffer_linea_buscar,5)) {
                return i;
            }
        }
    }

    return -1;

}

//Retorna 0 si no encontrada
int menu_view_basic_listing_find_line(zxvision_window *w)
{
	char buffer_texto[10];

	buffer_texto[0]=0;
    menu_ventana_scanf("Line to search",buffer_texto,6);

    int linea=parse_string_to_number(buffer_texto);

    int indice=menu_view_basic_listing_find_line_aux(linea);

    if (indice>=0) {
        //printf("Encontrada\n");
        zxvision_set_offset_y(w,indice);
        return 1;
    }

    else {
        //printf("No encontrada\n");
        return 0;
    }

}

//Retorna indice de linea en array si encontrada
//-1 si no encontrada
int menu_view_basic_listing_find_text_aux(char *texto_buscar,int linea_inicial)
{

    int i;


    for (i=0;i<menu_view_basic_listing_total_lineas;i++) {
        char *linea=menu_view_basic_listing_punteros_lineas[i];
        //printf("%d : %s\n",i,linea);

        if (i>=linea_inicial) {
            char *encontrado=util_strcasestr(linea,texto_buscar);
            if (encontrado!=NULL) {
                return i;
            }
        }

    }

    return -1;

}

char menu_view_basic_listing_find_text_buffer_texto[30]="";

int menu_view_basic_listing_find_text_last_line=0;

//Retorna 0 si no encontrado
int menu_view_basic_listing_find_next_continue(zxvision_window *w)
{

    int indice=menu_view_basic_listing_find_text_aux(menu_view_basic_listing_find_text_buffer_texto,menu_view_basic_listing_find_text_last_line);

    if (indice>=0) {
        //printf("Encontrada\n");
        menu_view_basic_listing_find_text_last_line=indice;
        zxvision_set_offset_y(w,indice);
        return 1;
    }

    else {
        menu_view_basic_listing_find_text_last_line=0;
        //printf("No encontrada\n");
        return 0;
    }

}

//Retorna 0 si no encontrado
int menu_view_basic_listing_find_next(zxvision_window *w)
{

    menu_view_basic_listing_find_text_last_line++;

    return menu_view_basic_listing_find_next_continue(w);

}


//Retorna 0 si no encontrado
int menu_view_basic_listing_find_text(zxvision_window *w)
{

    menu_ventana_scanf("Text to search",menu_view_basic_listing_find_text_buffer_texto,30);

    menu_view_basic_listing_find_text_last_line=0;

    return menu_view_basic_listing_find_next_continue(w);

}

void menu_view_basic_listing_print_pointer_line(zxvision_window *ventana)
{
    int antes_writing_inverse_color=ventana->writing_inverse_color;

    ventana->writing_inverse_color=1;

    char buffer_length[100];

    //No mostrar valores negativos, que queda extraño
    //Esto pasa por ejemplo al cargar Breakout de ZX80 y ejecutarlo
    if (menu_view_basic_listing_memory_length<0) {
        strcpy(buffer_length,"Unknown");
    }

    else {
        sprintf(buffer_length,"%d",menu_view_basic_listing_memory_length);
    }


    char buffer_custom_pointer[100];
    buffer_custom_pointer[0]=0;

    if (menu_view_basic_listing_memory_enabled.v) {
        sprintf(buffer_custom_pointer,"[Custom] S~~tart: %d len~~gth: %s",menu_view_basic_listing_memory_pointer,buffer_length);
    }
    else {
        sprintf(buffer_custom_pointer,"[ Auto ] Start: %d length: %s",menu_view_basic_listing_memory_pointer,buffer_length);
    }


    zxvision_print_string_defaults_fillspc_format(ventana,1,MENU_VIEW_BASIC_LISTING_LINE_POINTER,
        "~~Start & Length %s",
        //(menu_view_basic_listing_memory_enabled.v ? 'X' : ' ' ),
        buffer_custom_pointer
    );
    ventana->writing_inverse_color=antes_writing_inverse_color;

}

void menu_view_basic_listing_overlay(void)
{

    menu_speech_set_tecla_pulsada(); //Si no, envia continuamente todo ese texto a speech

    //si ventana minimizada, no ejecutar todo el codigo de overlay
    if (menu_view_basic_listing_window->is_minimized) return;


    //Print....
    //Tambien contar si se escribe siempre o se tiene en cuenta contador_segundo...
    //printf("contador_segundo %d\n",contador_segundo);

    //esto hara ejecutar esto 5 veces por segundo
    if ( ((contador_segundo%200) == 0 && menu_view_basic_contador_segundo_anterior!=contador_segundo) ) {

        menu_view_basic_contador_segundo_anterior=contador_segundo;

        view_basic_parameters parameters;

        //Llamar aqui por saber si ha cambiado la longitud del programa basic
        menu_view_basic_listing_get_basic_parameters(&parameters);

        //printf("start add: %d\n",menu_view_basic_listing_memory_pointer);

        //Escribir esto continuamente para reflejar cambios en inicio de programa cuando se usa Interface 1
        menu_view_basic_listing_print_pointer_line(menu_view_basic_listing_window);

        //Obtener linea actual
        int linea,sentencia;
        debug_view_basic_get_current_line(&linea,&sentencia);

        char buffer_linea[100];


        if (MACHINE_IS_ZX8081) {
            if (linea>32767) {
                strcpy(buffer_linea,"Not in line");
            }
            else {
                sprintf(buffer_linea,"%5d",linea);
            }
        }

        else {
            //Spectrum
            if (linea>32767) {
                sprintf(buffer_linea,"Not in line. Sentence %d",sentencia);
            }
            else {
                sprintf(buffer_linea,"%5d:%3d",linea,sentencia);
            }
        }


        //Muestro aqui continuamente la linea y sentencia actual

        zxvision_print_string_defaults_fillspc_format(menu_view_basic_listing_window,1,MENU_VIEW_BASIC_LISTING_LINE_LINE,"Current line: %s",buffer_linea);


        z80_long_int crc32=menu_view_basic_listing_get_crc32();
        //printf("Obtenido crc: %X\n",crc32);
        if (crc32!=menu_view_basic_listing_last_crc32) {
            //printf("CRC modificado\n");
            menu_view_basic_listing_last_crc32=crc32;
            menu_view_basic_listing_modified=1;
        }

        if (menu_view_basic_listing_follow_current_line.v) {


            //Ver si ha cambiado linea actual

            if (linea>=0 && linea<=9999) {

                if (linea!=menu_view_basic_listing_follow_line_last) {
                    menu_view_basic_listing_follow_line_last=linea;

                    int indice=menu_view_basic_listing_find_line_aux(menu_view_basic_listing_follow_line_last);

                    if (indice>=0) {
                        //printf("Encontrada linea actual %d\n",linea);
                        zxvision_set_offset_y(menu_view_basic_listing_window,indice);

                    }

                    else {
                        //printf("No encontrada\n");

                    }
                }
            }
        }


    }

    if (menu_view_basic_listing_modified) {
        //printf("Obtener listado de nuevo de nuevo\n");
        menu_view_basic_listing_modified=0;

        if (menu_view_basic_listing_results_buffer!=NULL) {
            free(menu_view_basic_listing_results_buffer);
            menu_view_basic_listing_results_buffer=NULL;
        }

        if (menu_view_basic_listing_results_buffer==NULL) {
            menu_view_basic_listing_results_buffer=util_malloc(VIEW_BASIC_MAX_BASIC_TEXT,"Can not allocate memory for view basic");
        }

        menu_view_basic_listing_get_basic(menu_view_basic_listing_results_buffer,0);
        menu_view_basic_listing_print_basic(menu_view_basic_listing_results_buffer,menu_view_basic_listing_window);

    }


    //Mostrar contenido
    zxvision_draw_window_contents(menu_view_basic_listing_window);

}




//Almacenar la estructura de ventana aqui para que se pueda referenciar desde otros sitios
zxvision_window zxvision_window_view_basic_listing;

int menu_view_basic_listing_set_number(char *title,int value)
{

    char string_texto[MAX_BREAKPOINT_CONDITION_LENGTH];
	sprintf(string_texto,"%d",value);

    menu_ventana_scanf(title,string_texto,MAX_BREAKPOINT_CONDITION_LENGTH);


	//int exp_par_evaluate_expression(char *entrada,char *salida)
	char buffer_salida[256]; //mas que suficiente
	char string_detoken[MAX_BREAKPOINT_CONDITION_LENGTH];

	int result=exp_par_evaluate_expression(string_texto,buffer_salida,string_detoken);
	if (result==0) {
		//menu_generic_message_format("Result","Parsed string: %s\nResult: %s",string_detoken,buffer_salida);
        value=parse_string_to_number(buffer_salida);
	}

	else if (result==1) {
		menu_error_message(buffer_salida);
	}

	else {
		menu_generic_message_format("Error","%s parsed string: %s",buffer_salida,string_detoken);
	}

    return value;

}

void menu_view_basic_listing_set_pointer(void)
{
    menu_view_basic_listing_memory_pointer=menu_view_basic_listing_set_number("Pointer",menu_view_basic_listing_memory_pointer);
}

void menu_view_basic_listing_set_length(void)
{
    menu_view_basic_listing_memory_length=menu_view_basic_listing_set_number("Length",menu_view_basic_listing_memory_length);
}




void menu_view_basic_listing(MENU_ITEM_PARAMETERS)
{
	menu_espera_no_tecla();

    if (!menu_multitarea) {
        menu_warn_message("This window needs multitask enabled");
        return;
    }

    zxvision_window *ventana;
    ventana=&zxvision_window_view_basic_listing;

	//IMPORTANTE! no crear ventana si ya existe. Esto hay que hacerlo en todas las ventanas que permiten background.
	//si no se hiciera, se crearia la misma ventana, y en la lista de ventanas activas , al redibujarse,
	//la primera ventana repetida apuntaria a la segunda, que es el mismo puntero, y redibujaria la misma, y se quedaria en bucle colgado
	//zxvision_delete_window_if_exists(ventana);

    //Crear ventana si no existe
    if (!zxvision_if_window_already_exists(ventana)) {
        int xventana,yventana,ancho_ventana,alto_ventana,is_minimized,is_maximized,ancho_antes_minimize,alto_antes_minimize;

        if (!util_find_window_geometry("viewbasiclisting",&xventana,&yventana,&ancho_ventana,&alto_ventana,&is_minimized,&is_maximized,&ancho_antes_minimize,&alto_antes_minimize)) {
            ancho_ventana=30;
            alto_ventana=20;

            xventana=menu_center_x()-ancho_ventana/2;
            yventana=menu_center_y()-alto_ventana/2;
        }


        zxvision_new_window_gn_cim(ventana,xventana,yventana,ancho_ventana,alto_ventana,ancho_ventana-1,alto_ventana-2,"View Basic Listing",
            "viewbasiclisting",is_minimized,is_maximized,ancho_antes_minimize,alto_antes_minimize);

        ventana->can_be_backgrounded=1;

        //unas lineas superiores fijas
        ventana->upper_margin=VIEW_BASIC_HEADER_LINES;

    }

    //Si ya existe, activar esta ventana
    else {
        zxvision_activate_this_window(ventana);
    }

	zxvision_draw_window(ventana);

	z80_byte tecla;


	int salir=0;


    menu_view_basic_listing_window=ventana; //Decimos que el overlay lo hace sobre la ventana que tenemos aqui


    //cambio overlay
    zxvision_set_window_overlay(ventana,menu_view_basic_listing_overlay);


    //Toda ventana que este listada en zxvision_known_window_names_array debe permitir poder salir desde aqui
    //Se sale despues de haber inicializado overlay y de cualquier otra variable que necesite el overlay
    if (zxvision_currently_restoring_windows_on_start) {
        //printf ("Saliendo de ventana ya que la estamos restaurando en startup\n");
        return;
    }

    menu_view_basic_listing_modified=1;

    do {



        ventana->writing_inverse_color=1;

        char buffer_betabasic[100]="";
        if (MACHINE_IS_SPECTRUM) {
            sprintf(buffer_betabasic,"[%c] Show ~~BetaBasic ",(debug_view_basic_show_betabasic.v ? 'X' : ' ') );
        }


        zxvision_print_string_defaults_fillspc_format(ventana,1,MENU_VIEW_BASIC_LISTING_LINE_SETTINGS,
            "[%c] Show ~~address %s[%c] Fo~~llow line",
            (debug_view_basic_show_address.v ? 'X' : ' ' ),
            buffer_betabasic,
            (menu_view_basic_listing_follow_current_line.v ? 'X' : ' ')
        );

        zxvision_print_string_defaults_fillspc_format(ventana,1,MENU_VIEW_BASIC_LISTING_LINE_FIND,
            "~~e: Find lin~~e ~~f: ~~Find text ~~n: Find ~~next"
        );

        menu_view_basic_listing_print_pointer_line(ventana);


        ventana->writing_inverse_color=0;




		tecla=zxvision_common_getkey_refresh();

        zxvision_handle_cursors_pgupdn(ventana,tecla);


        switch (tecla) {
            case 'a':
                debug_view_basic_show_address.v ^=1;
                menu_view_basic_listing_modified=1;
            break;

            case 'b':
                if (MACHINE_IS_SPECTRUM) {
                    debug_view_basic_show_betabasic.v ^=1;
                    menu_view_basic_listing_modified=1;
                }
            break;

            case 'e':
                if (!menu_view_basic_listing_find_line(ventana)) {
                    menu_warn_message("Line not found");
                }
            break;

            case 'f':
                if (!menu_view_basic_listing_find_text(ventana)) {
                    menu_warn_message("Text not found");
                }
            break;

            case 'g':
                if (menu_view_basic_listing_memory_enabled.v) {
                    menu_view_basic_listing_set_length();
                }
            break;

            case 'l':
                menu_view_basic_listing_follow_current_line.v ^=1;
                menu_view_basic_listing_follow_line_last=-1;
            break;



            case 'n':
                if (menu_view_basic_listing_find_text_buffer_texto[0]==0) {
                    menu_warn_message("No text to search");
                }
                else {
                    if (!menu_view_basic_listing_find_next(ventana)) {
                        menu_warn_message("Text not found");
                    }
                }
            break;

            case 's':
                menu_view_basic_listing_memory_enabled.v ^=1;
            break;

            case 't':
                if (menu_view_basic_listing_memory_enabled.v) {
                    menu_view_basic_listing_set_pointer();
                }
            break;


            //Salir con ESC
            case 2:
                salir=1;
            break;

            //O tecla background
            case 3:
                salir=1;
            break;
        }


    } while (salir==0);


	util_add_window_geometry_compact(ventana);

	if (tecla==3) {
		zxvision_message_put_window_background();
	}

	else {
		zxvision_destroy_window(ventana);
	}


}







void menu_debug_save_binary(MENU_ITEM_PARAMETERS)
{

	char *filtros[2];

	filtros[0]="";
	filtros[1]=0;


	//guardamos directorio actual
	char directorio_actual[PATH_MAX];
	getcwd(directorio_actual,PATH_MAX);

	int ret;

	//Obtenemos ultimo directorio visitado
	if (binary_file_save[0]!=0) {
		char directorio[PATH_MAX];
		util_get_dir(binary_file_save,directorio);
		//printf ("strlen directorio: %d directorio: %s\n",strlen(directorio),directorio);

		//cambiamos a ese directorio, siempre que no sea nulo
		if (directorio[0]!=0) {
				debug_printf (VERBOSE_INFO,"Changing to last directory: %s",directorio);
				zvfs_chdir(directorio);
		}
	}

	ret=menu_filesel_save("Select File to Save",filtros,binary_file_save);

	//volvemos a directorio inicial
	zvfs_chdir(directorio_actual);


    if (ret==1) {

		//Ver si archivo existe y preguntar

      	if (si_existe_archivo(binary_file_save)) {

			if (menu_confirm_yesno_texto("File exists","Overwrite?")==0) return;

		}



		menu_debug_change_memory_zone();

		char string_direccion[10];
		sprintf (string_direccion,"%XH",save_binary_last_address);
		menu_ventana_scanf("Address: ",string_direccion,10);
		int valor_leido_direccion=parse_string_to_number(string_direccion);
		save_binary_last_address=valor_leido_direccion;


		char string_longitud[8];
		sprintf (string_longitud,"%d",save_binary_last_length);
		menu_ventana_scanf("Length: 0 - all",string_longitud,8);
		int valor_leido_longitud=parse_string_to_number(string_longitud);
		save_binary_last_length=valor_leido_longitud;


		save_binary_file(binary_file_save,valor_leido_direccion,valor_leido_longitud);


		//Y salimos de todos los menus
        salir_todos_menus=1;

    }


}




void menu_debug_file_utils(MENU_ITEM_PARAMETERS)
{

    int ret=1;

    while (ret!=0) {
        char *filtros[2];

        filtros[0]="";
        filtros[1]=0;

        //guardamos directorio actual
        char directorio_actual[PATH_MAX];
        getcwd(directorio_actual,PATH_MAX);


        //Obtenemos ultimo directorio visitado
        if (file_utils_file_name[0]!=0) {
            char directorio[PATH_MAX];
            util_get_dir(file_utils_file_name,directorio);
            //printf ("strlen directorio: %d directorio: %s\n",strlen(directorio),directorio);

            //cambiamos a ese directorio, siempre que no sea nulo
            if (directorio[0]!=0) {
                    debug_printf (VERBOSE_INFO,"Changing to last directory: %s",directorio);
                    zvfs_chdir(directorio);
            }
        }

        menu_filesel_show_utils.v=1;
        ret=menu_filesel("File utilities",filtros,file_utils_file_name);
        menu_filesel_show_utils.v=0;

        //volvemos a directorio inicial
        zvfs_chdir(directorio_actual);
    }

}



int menu_debug_view_basic_cond(void)
{

	if (MACHINE_IS_ACE) return 0;
    if (MACHINE_IS_COLECO) return 0;
	if (MACHINE_IS_CPC) return 0;
    if (MACHINE_IS_MK14) return 0;
    if (MACHINE_IS_MSX) return 0;
    if (MACHINE_IS_QL) return 0;
	if (MACHINE_IS_SAM) return 0;
    if (MACHINE_IS_SG1000) return 0;
    if (MACHINE_IS_SMS) return 0;
    if (MACHINE_IS_SVI) return 0;
    if (MACHINE_IS_Z88) return 0;
	return 1;
}

void menu_debug_spritefinder(MENU_ITEM_PARAMETERS)
{
	if (spritefinder_enabled.v) spritefinder_disable();
	else spritefinder_enable();
}




void menu_write_message(MENU_ITEM_PARAMETERS)
{
	char buffer_texto[256];

	buffer_texto[0]=0;
    menu_ventana_scanf("Just write...",buffer_texto,256);

	menu_generic_message("Your message",buffer_texto);


}


#ifdef TIMESENSORS_ENABLED

void menu_debug_timesensors(MENU_ITEM_PARAMETERS)
{
	int i;


    char *timesensors_buffer=util_malloc_max_texto_generic_message("Can not allocate memory for debug timesensors");

	char buf_linea[64];

    int index_buffer=0;

    for (i=0;i<10;i++) {
		long media=TIMESENSOR_ENTRY_MEDIATIME(i);
        sprintf (buf_linea,"ID: %02d average: %ld us (%ld ms)\n",i,media,media/1000);
        sprintf (&timesensors_buffer[index_buffer],"%s",buf_linea); index_buffer +=strlen(buf_linea);

		long max=TIMESENSOR_ENTRY_MAXTIME(i);
        sprintf (buf_linea,"ID: %02d max: %ld us (%ld ms)\n",i,max,max/1000);
        sprintf (&timesensors_buffer[index_buffer],"%s",buf_linea); index_buffer +=strlen(buf_linea);
	}

	menu_generic_message("Sensors",timesensors_buffer);

    free(timesensors_buffer);


}

void menu_debug_timesensors_enable(MENU_ITEM_PARAMETERS)
{
	timesensors_started ^=1;

}

void menu_debug_timesensors_init(MENU_ITEM_PARAMETERS)
{
	TIMESENSOR_INIT();
}

#endif

void menu_debug_set_pc_zero(MENU_ITEM_PARAMETERS)
{
	if (menu_confirm_yesno("Set PC=0")==1) {
		reg_pc=0;
	}

    //Y salimos de todos los menus
    salir_todos_menus=1;

}


#define SHORTCUTS_HELPER_ANCHO 32
#define SHORTCUTS_HELPER_ALTO 3
#define SHORTCUTS_HELPER_X (menu_center_x()-SHORTCUTS_HELPER_ANCHO/2)

//ponerlo arriba para que inicialmente sea el sitio mejor para verse
//pero por debajo de los botones aproximadamente
#define SHORTCUTS_HELPER_Y 4


//Usado dentro del overlay de menu_shortcuts_helper_overlay, para mostrar dos veces por segundo el texto que average, etc
int menu_shortcuts_helper_valor_contador_segundo_anterior;


zxvision_window *menu_audio_shortcuts_helper_window;



void menu_shortcuts_helper_overlay(void)
{



	menu_speech_set_tecla_pulsada(); //Si no, envia continuamente todo ese texto a speech

    //si ventana minimizada, no ejecutar todo el codigo de overlay
    if (menu_audio_shortcuts_helper_window->is_minimized) return;

    //printf ("contador_segundo=%d\n",contador_segundo);


    //esto hara ejecutar esto 5 veces por segundo
    if ( ((contador_segundo%200) == 0 && menu_shortcuts_helper_valor_contador_segundo_anterior!=contador_segundo) || menu_multitarea==0) {
        menu_shortcuts_helper_valor_contador_segundo_anterior=contador_segundo;

        zxvision_print_string_defaults_fillspc(menu_audio_shortcuts_helper_window,1,0,zxvision_helper_shorcuts_accumulated);
	}


	zxvision_draw_window_contents(menu_audio_shortcuts_helper_window);

}



zxvision_window zxvision_window_shortcuts_helper;


void menu_shortcuts_window(MENU_ITEM_PARAMETERS)
{

    menu_espera_no_tecla();
    menu_reset_counters_tecla_repeticion();


    zxvision_window *ventana;
    ventana=&zxvision_window_shortcuts_helper;


	//IMPORTANTE! no crear ventana si ya existe. Esto hay que hacerlo en todas las ventanas que permiten background.
	//si no se hiciera, se crearia la misma ventana, y en la lista de ventanas activas , al redibujarse,
	//la primera ventana repetida apuntaria a la segunda, que es el mismo puntero, y redibujaria la misma, y se quedaria en bucle colgado
	//zxvision_delete_window_if_exists(ventana);

    //Crear ventana si no existe
    if (!zxvision_if_window_already_exists(ventana)) {

        int x,y,ancho,alto,is_minimized,is_maximized,ancho_antes_minimize,alto_antes_minimize;

        if (!util_find_window_geometry("shortcutshelper",&x,&y,&ancho,&alto,&is_minimized,&is_maximized,&ancho_antes_minimize,&alto_antes_minimize)) {
            x=SHORTCUTS_HELPER_X;
            y=SHORTCUTS_HELPER_Y;
            ancho=SHORTCUTS_HELPER_ANCHO;
            alto=SHORTCUTS_HELPER_ALTO;
        }


        zxvision_new_window_gn_cim(ventana,x,y,ancho,alto,ancho-1,alto-2,"Shortcuts helper","shortcutshelper",is_minimized,is_maximized,ancho_antes_minimize,alto_antes_minimize);

        ventana->can_be_backgrounded=1;
        //indicar nombre del grabado de geometria
        //strcpy(ventana->geometry_name,"shortcutshelper");
        //restaurar estado minimizado de ventana
        //ventana->is_minimized=is_minimized;

        //printf("despues zxvision_new_window_nocheck_staticsize\n");

    }

    //Si ya existe, activar esta ventana
    else {

        zxvision_activate_this_window(ventana);
    }

	zxvision_draw_window(ventana);


    //Cambiamos funcion overlay de texto de menu

    //cambio overlay
    zxvision_set_window_overlay(ventana,menu_shortcuts_helper_overlay);

	menu_audio_shortcuts_helper_window=ventana; //Decimos que el overlay lo hace sobre la ventana que tenemos aqui

	//Toda ventana que este listada en zxvision_known_window_names_array debe permitir poder salir desde aqui
	//Se sale despues de haber inicializado overlay y de cualquier otra variable que necesite el overlay
	if (zxvision_currently_restoring_windows_on_start) {
		//printf ("Saliendo de ventana ya que la estamos restaurando en startup\n");
		return;
	}

    int tecla=zxvision_wait_until_esc(ventana);



    //En caso de menus tabulados, suele ser necesario esto. Si no, la ventana se quedaria visible


    //Grabar geometria ventana
    util_add_window_geometry_compact(ventana);


    if (tecla==3) {
        zxvision_message_put_window_background();
    }

    else {
        //En caso de menus tabulados, es responsabilidad de este de liberar ventana
        zxvision_destroy_window(ventana);
    }

}


void menu_debug_machine_info(MENU_ITEM_PARAMETERS)
{

    char *text_buffer=util_malloc_max_texto_generic_message("Can not allocate memory for showing stats");
    //Empezar con cadena vacia para poder concatenar
    text_buffer[0]=0;

	//margen suficiente para que quepa una linea ...
	char buf_linea[100];

    //Nombre maquina
    sprintf(buf_linea,"Machine: %s\n",get_machine_name(current_machine_type));
    util_concat_string(text_buffer,buf_linea,MAX_TEXTO_GENERIC_MESSAGE);

    sprintf(buf_linea,"Family: %s\n",debug_machine_info_family(current_machine_type));
    util_concat_string(text_buffer,buf_linea,MAX_TEXTO_GENERIC_MESSAGE);

    int fabricante=return_fabricante_maquina(current_machine_type);
    char fabricante_texto[100];
    menu_convierte_texto_sin_modificadores(array_fabricantes_hotkey[fabricante],fabricante_texto);
    sprintf(buf_linea,"Manufacturer: %s\n",fabricante_texto);
    util_concat_string(text_buffer,buf_linea,MAX_TEXTO_GENERIC_MESSAGE);


    //CPU
    if (CPU_IS_MOTOROLA) {
        strcpy(buf_linea,"CPU: Motorola 68008\n"); //TODO: de momento no hay otras maquinas emuladas que usen cpu diferente del 68008
    }
    else if (CPU_IS_SCMP) {
        strcpy(buf_linea,"CPU: National Semiconductor SC/MP (INS8060)\n");
    }
    else {
        if (MACHINE_IS_ZXUNO || MACHINE_IS_TBBLUE || MACHINE_IS_PRISM ) {
            sprintf(buf_linea,"CPU: Zilog Z80 compatible (FPGA)\n");
        }
        else sprintf(buf_linea,"CPU: Zilog Z80 (%s)\n",z80_cpu_types_strings[z80_cpu_current_type]);
    }
    util_concat_string(text_buffer,buf_linea,MAX_TEXTO_GENERIC_MESSAGE);

    //Memoria RAM
    int total_ram=get_ram_size();


    if (total_ram>=1024) {
        sprintf(buf_linea,"Total RAM: %d KB\n",total_ram/1024);
    }
    else {
        sprintf(buf_linea,"Total RAM: %d B\n",total_ram);
    }
    util_concat_string(text_buffer,buf_linea,MAX_TEXTO_GENERIC_MESSAGE);


    //Memoria ROM
    int total_rom=get_rom_size(current_machine_type);

    if (total_rom>=1024) {
        sprintf(buf_linea,"Total ROM: %d KB\n",total_rom/1024);
    }
    else {
        sprintf(buf_linea,"Total ROM: %d B\n",total_rom);
    }
    util_concat_string(text_buffer,buf_linea,MAX_TEXTO_GENERIC_MESSAGE);

	menu_generic_message("Machine Information",text_buffer);

    free(text_buffer);
}



zxvision_window *menu_ascii_table_window;

#define ASCII_TABLE_CHARS_PER_LINE 4
#define ASCII_TABLE_TOTAL_CHARS_TO_SHOW (127-32)
#define ASCII_TABLE_TOTAL_LINES ((ASCII_TABLE_TOTAL_CHARS_TO_SHOW/ASCII_TABLE_CHARS_PER_LINE)+1)
//De cada caracter, 6 caracteres + espacio
//XXX: B
#define ASCII_TABLE_LENGTH_PER_CHAR 7
#define ASCII_TABLE_LINE_LENGTH (ASCII_TABLE_LENGTH_PER_CHAR*ASCII_TABLE_CHARS_PER_LINE)

int menu_ascii_table_modo_vertical=1;
int menu_ascii_table_modo_decimal=1;

void menu_ascii_table_overlay(void)
{

    menu_speech_set_tecla_pulsada(); //Si no, envia continuamente todo ese texto a speech

    //si ventana minimizada, no ejecutar todo el codigo de overlay
    if (menu_ascii_table_window->is_minimized) return;


    //Print....
    //Tambien contar si se escribe siempre o se tiene en cuenta contador_segundo...

    int i;
    int linea=0;
    int col=0;


    for (i=32;i<=126;i++) {

        int x,y;

        //En horizontal:
        if (!menu_ascii_table_modo_vertical) {
            x=(col*ASCII_TABLE_LENGTH_PER_CHAR)+1;
            y=linea+1;

            col ++;
            if (col==ASCII_TABLE_CHARS_PER_LINE) {
                col=0;
                linea++;
            }

        }

        //En vertical:
        else {
            x=col+1;
            y=linea+1;

            linea++;
            if (linea==ASCII_TABLE_TOTAL_LINES) {
                linea=0;
                col +=ASCII_TABLE_LENGTH_PER_CHAR;
            }

        }

        if (menu_ascii_table_modo_decimal) {
            zxvision_print_string_defaults_format(menu_ascii_table_window,x,y,"%3d: %c ",i,i);
        }
        else {
            zxvision_print_string_defaults_format(menu_ascii_table_window,x,y," %02X: %c ",i,i);
        }


    }


    //Mostrar colores
    zxvision_draw_window_contents(menu_ascii_table_window);

}




//Almacenar la estructura de ventana aqui para que se pueda referenciar desde otros sitios
zxvision_window zxvision_window_ascii_table;


void menu_ascii_table(MENU_ITEM_PARAMETERS)
{
	menu_espera_no_tecla();

    if (!menu_multitarea) {
        menu_warn_message("This window needs multitask enabled");
        return;
    }

    zxvision_window *ventana;
    ventana=&zxvision_window_ascii_table;

	//IMPORTANTE! no crear ventana si ya existe. Esto hay que hacerlo en todas las ventanas que permiten background.
	//si no se hiciera, se crearia la misma ventana, y en la lista de ventanas activas , al redibujarse,
	//la primera ventana repetida apuntaria a la segunda, que es el mismo puntero, y redibujaria la misma, y se quedaria en bucle colgado
	//zxvision_delete_window_if_exists(ventana);

    //Crear ventana si no existe
    if (!zxvision_if_window_already_exists(ventana)) {
        int xventana,yventana,ancho_ventana,alto_ventana,is_minimized,is_maximized,ancho_antes_minimize,alto_antes_minimize;

        if (!util_find_window_geometry("asciitable",&xventana,&yventana,&ancho_ventana,&alto_ventana,&is_minimized,&is_maximized,&ancho_antes_minimize,&alto_antes_minimize)) {
            ancho_ventana=ASCII_TABLE_LINE_LENGTH+1;
            alto_ventana=ASCII_TABLE_TOTAL_LINES+2+1;

            xventana=menu_center_x()-ancho_ventana/2;
            yventana=menu_center_y()-alto_ventana/2;
        }


        zxvision_new_window_gn_cim(ventana,xventana,yventana,ancho_ventana,alto_ventana,ancho_ventana-1,alto_ventana-2,"Ascii Table",
            "asciitable",is_minimized,is_maximized,ancho_antes_minimize,alto_antes_minimize);

        ventana->can_be_backgrounded=1;

    }

    //Si ya existe, activar esta ventana
    else {
        zxvision_activate_this_window(ventana);
    }

	zxvision_draw_window(ventana);

	z80_byte tecla;


	int salir=0;


    menu_ascii_table_window=ventana; //Decimos que el overlay lo hace sobre la ventana que tenemos aqui


    //cambio overlay
    zxvision_set_window_overlay(ventana,menu_ascii_table_overlay);


    //Toda ventana que este listada en zxvision_known_window_names_array debe permitir poder salir desde aqui
    //Se sale despues de haber inicializado overlay y de cualquier otra variable que necesite el overlay
    if (zxvision_currently_restoring_windows_on_start) {
            //printf ("Saliendo de ventana ya que la estamos restaurando en startup\n");
            return;
    }

    do {


        z80_bit antes_menu_writing_inverse_color;
        antes_menu_writing_inverse_color.v=menu_writing_inverse_color.v;

        //Forzar a mostrar atajos
        menu_writing_inverse_color.v=1;


        zxvision_print_string_defaults_fillspc_format(menu_ascii_table_window,1,0,"~~Orien.: %s ~~Mode: [%s]",
            (menu_ascii_table_modo_vertical ? "[Vert] " : "[Horiz]"),
            (menu_ascii_table_modo_decimal ? "Dec" : "Hex")

        );

        //Restaurar mostrar atajos
        menu_writing_inverse_color.v=antes_menu_writing_inverse_color.v;



		tecla=zxvision_common_getkey_refresh();


        switch (tecla) {

            case 'o':
                menu_ascii_table_modo_vertical ^=1;
            break;

            case 'm':
                menu_ascii_table_modo_decimal ^=1;
            break;

            //Salir con ESC
            case 2:
                salir=1;
            break;

            //O tecla background
            case 3:
                salir=1;
            break;
        }


    } while (salir==0);


	util_add_window_geometry_compact(ventana);

	if (tecla==3) {
		zxvision_message_put_window_background();
	}

	else {

		zxvision_destroy_window(ventana);
	}


}




zxvision_window *menu_view_gosub_stack_window;

int menu_view_gosub_stack_recargar=0;

#define MENU_VIEW_GOSUB_STACK_MAX_LINE_LENGTH 30

z80_long_int menu_view_gosub_stack_last_crc32=0;

//Calcular crc32 del stack por saber si se modifica para volver a renderizar
z80_long_int menu_view_gosub_stack_get_crc32(void)
{


    int start_address=reg_sp;
    int length=65536-reg_sp;

    if (length<1) {
        //escribir_socket(misocket,"ERROR. Length must be >0");
        return 0;
    }


    //Copiar contenido memoria segun memory zone activa a buffer de memoria temporal
    z80_byte *memoria_temporal;
    memoria_temporal=malloc(length);
    if (memoria_temporal==NULL) cpu_panic("Can not allocate memory for crc32 calculation");

    int longitud_copiar=length;
    int i;

    for (i=0;longitud_copiar>0;i++,longitud_copiar--) {
        z80_byte byte_leido=peek_byte_no_time(start_address+i);
        memoria_temporal[i]=byte_leido;
    }



    z80_long_int crc32=util_crc32_calculation(0,memoria_temporal,length);


    free(memoria_temporal);

    return crc32;

}



void menu_view_gosub_stack_overlay(void)
{

    menu_speech_set_tecla_pulsada(); //Si no, envia continuamente todo ese texto a speech

    //si ventana minimizada, no ejecutar todo el codigo de overlay
    if (menu_view_gosub_stack_window->is_minimized) return;

    if (!MACHINE_IS_SPECTRUM) {
        zxvision_cls(menu_view_gosub_stack_window);
        zxvision_print_string_defaults_fillspc_format(menu_view_gosub_stack_window,1,0,"Only available on Spectrum");
    }

    else {


        //Print....
        //Tambien contar si se escribe siempre o se tiene en cuenta contador_segundo...

        z80_long_int crc32=menu_view_gosub_stack_get_crc32();
        //printf("Obtenido crc: %X\n",crc32);
        if (crc32!=menu_view_gosub_stack_last_crc32) {
            //printf("CRC modificado\n");
            menu_view_gosub_stack_last_crc32=crc32;
            menu_view_gosub_stack_recargar=1;
        }

        if (menu_view_gosub_stack_recargar) {
            char *results_buffer=util_malloc_max_texto_generic_message("Can not allocate memory for showing go sub stack");

            int lineas=debug_view_basic_gosub_stack(results_buffer,MAX_TEXTO_GENERIC_MESSAGE);

            //Mostrar una a una todas las lineas

            //Ajustar alto ventana. Al menos una linea aunque solo sea para mostrar error
            zxvision_set_total_height(menu_view_gosub_stack_window,lineas+1);

            char buffer_linea[MENU_VIEW_GOSUB_STACK_MAX_LINE_LENGTH+1];
            char *puntero_leer=results_buffer;
            int i;
            for (i=0;i<menu_view_gosub_stack_window->total_height;i++) {
                int columna=0;
                while (*puntero_leer && (*puntero_leer)!='\n') {
                    if (columna<MENU_VIEW_GOSUB_STACK_MAX_LINE_LENGTH) {
                        buffer_linea[columna++]=*puntero_leer;
                    }
                    puntero_leer++;
                }
                buffer_linea[columna++]=0;
                if (*puntero_leer=='\n') puntero_leer++;

                zxvision_print_string_defaults_fillspc_format(menu_view_gosub_stack_window,1,i,"%s",buffer_linea);
            }


            free(results_buffer);

            menu_view_gosub_stack_recargar=0;

        }
    }

    //Mostrar contenido
    zxvision_draw_window_contents(menu_view_gosub_stack_window);

}




//Almacenar la estructura de ventana aqui para que se pueda referenciar desde otros sitios
zxvision_window zxvision_window_view_gosub_stack;

//Nota: menu_view_basic_variables y menu_view_gosub_stack son casi la misma ventana, se comportan igual,
//solo difieren en qué información muestran. Quizá en un futuro se podría hacer una ventana genérica que pudiese
//mostrar un contenido de texto en background y que refrescase bajo ciertas condiciones
void menu_view_gosub_stack(MENU_ITEM_PARAMETERS)
{
	menu_espera_no_tecla();

    if (!menu_multitarea) {
        menu_warn_message("This window needs multitask enabled");
        return;
    }

    zxvision_window *ventana;
    ventana=&zxvision_window_view_gosub_stack;

	//IMPORTANTE! no crear ventana si ya existe. Esto hay que hacerlo en todas las ventanas que permiten background.
	//si no se hiciera, se crearia la misma ventana, y en la lista de ventanas activas , al redibujarse,
	//la primera ventana repetida apuntaria a la segunda, que es el mismo puntero, y redibujaria la misma, y se quedaria en bucle colgado
	//zxvision_delete_window_if_exists(ventana);

    //Crear ventana si no existe
    if (!zxvision_if_window_already_exists(ventana)) {
        int xventana,yventana,ancho_ventana,alto_ventana,is_minimized,is_maximized,ancho_antes_minimize,alto_antes_minimize;

        if (!util_find_window_geometry("viewgosubstack",&xventana,&yventana,&ancho_ventana,&alto_ventana,&is_minimized,&is_maximized,&ancho_antes_minimize,&alto_antes_minimize)) {
            ancho_ventana=30;
            alto_ventana=20;

            xventana=menu_center_x()-ancho_ventana/2;
            yventana=menu_center_y()-alto_ventana/2;
        }


        zxvision_new_window_gn_cim(ventana,xventana,yventana,ancho_ventana,alto_ventana,ancho_ventana-1,alto_ventana-2,"GO SUB stack",
            "viewgosubstack",is_minimized,is_maximized,ancho_antes_minimize,alto_antes_minimize);

        ventana->can_be_backgrounded=1;

    }

    //Si ya existe, activar esta ventana
    else {
        zxvision_activate_this_window(ventana);
    }

	zxvision_draw_window(ventana);

	z80_byte tecla;


	int salir=0;


    menu_view_gosub_stack_recargar=1;

    menu_view_gosub_stack_window=ventana; //Decimos que el overlay lo hace sobre la ventana que tenemos aqui


    //cambio overlay
    zxvision_set_window_overlay(ventana,menu_view_gosub_stack_overlay);


    //Toda ventana que este listada en zxvision_known_window_names_array debe permitir poder salir desde aqui
    //Se sale despues de haber inicializado overlay y de cualquier otra variable que necesite el overlay
    if (zxvision_currently_restoring_windows_on_start) {
        //printf ("Saliendo de ventana ya que la estamos restaurando en startup\n");
        return;
    }

    do {


		tecla=zxvision_common_getkey_refresh();
        zxvision_handle_cursors_pgupdn(ventana,tecla);

        switch (tecla) {


            //Salir con ESC
            case 2:
                salir=1;
            break;

            //O tecla background
            case 3:
                salir=1;
            break;
        }


    } while (salir==0);


	util_add_window_geometry_compact(ventana);

	if (tecla==3) {
		zxvision_message_put_window_background();
	}

	else {
		zxvision_destroy_window(ventana);
	}


}





void menu_debug_view_basic(MENU_ITEM_PARAMETERS)
{
    menu_item *array_menu_common;
    menu_item item_seleccionado;
    int retorno_menu;


    do {


		menu_add_item_menu_en_es_ca_inicial(&array_menu_common,MENU_OPCION_NORMAL,menu_view_basic_listing,menu_debug_view_basic_cond,
            "View Basic ~~listing","Ver ~~listado Basic","Veure ~~llistat Basic");
		menu_add_item_menu_shortcut(array_menu_common,'l');
        menu_add_item_menu_se_cerrara(array_menu_common);
        menu_add_item_menu_genera_ventana(array_menu_common);

        if (MACHINE_IS_SPECTRUM || MACHINE_IS_ZX8081) {
		    menu_add_item_menu_en_es_ca(array_menu_common,MENU_OPCION_NORMAL,menu_view_basic_variables,NULL,
                "View Basic ~~variables","Ver ~~variables Basic","Veure ~~variables Basic");
            menu_add_item_menu_shortcut(array_menu_common,'v');
            menu_add_item_menu_se_cerrara(array_menu_common);
            menu_add_item_menu_genera_ventana(array_menu_common);
        }

        if (MACHINE_IS_SPECTRUM) {
		    menu_add_item_menu_en_es_ca(array_menu_common,MENU_OPCION_NORMAL,menu_view_gosub_stack,NULL,
                "View ~~GO SUB stack","Ver pila ~~GO SUB","Veure pila ~~GO SUB");
            menu_add_item_menu_shortcut(array_menu_common,'g');
            menu_add_item_menu_se_cerrara(array_menu_common);
            menu_add_item_menu_genera_ventana(array_menu_common);
        }



        menu_add_item_menu_separator(array_menu_common);

        menu_add_ESC_item(array_menu_common);


        //Nota: si no se agrega el nombre del path del indice, se generará uno automáticamente
        menu_add_item_menu_index_full_path(array_menu_common,
            "Main Menu-> Debug-> Basic","Menú Principal-> Debug-> Basic","Menú Principal-> Debug-> Basic");

        retorno_menu=menu_dibuja_menu(&debug_view_basic_opcion_seleccionada,&item_seleccionado,array_menu_common,
            "Basic","Basic","Basic" );

        if ((item_seleccionado.tipo_opcion&MENU_OPCION_ESC)==0 && retorno_menu>=0) {
            //llamamos por valor de funcion
            if (item_seleccionado.menu_funcion!=NULL) {
                //printf ("actuamos por funcion\n");
                item_seleccionado.menu_funcion(item_seleccionado.valor_opcion);

            }
        }

    } while ( (item_seleccionado.tipo_opcion&MENU_OPCION_ESC)==0 && retorno_menu!=MENU_RETORNO_ESC && !salir_todos_menus);

}



//menu debug
void menu_debug_main(MENU_ITEM_PARAMETERS)
{
    menu_item *array_menu_debug;
    menu_item item_seleccionado;
	int retorno_menu;

    //letras usadas: rhndmictxbsvfpoaug
    do {
        menu_add_item_menu_inicial(&array_menu_debug,"~~Reset",MENU_OPCION_NORMAL,menu_debug_reset,NULL);
		menu_add_item_menu_shortcut(array_menu_debug,'r');
        menu_add_item_menu_genera_ventana(array_menu_debug);

        //Agregar una acción de solo hacer PC=0 en casos, por ejemplo, en que hay divide en modo mapram y no queremos
        //que se reinicialice toda la maquina y perder ese mapeo
        //Dejamos que la acción siempre esté visible
        if (CPU_IS_Z80) {
            menu_add_item_menu_en_es_ca(array_menu_debug,MENU_OPCION_NORMAL,menu_debug_set_pc_zero,NULL,
                "Set PC=0","Establecer PC=0","Establir PC=0");
            menu_add_item_menu_tooltip(array_menu_debug,"It only resets cpu by setting PC register to zero");
            menu_add_item_menu_ayuda(array_menu_debug,"It only resets cpu by setting PC register to zero.\n"
                "Useful for example using DivIDE firmwares in MAPRAM mode and you need to restart it but without "
                "losing the mapping");
            menu_add_item_menu_genera_ventana(array_menu_debug);
            menu_add_item_menu_es_avanzado(array_menu_debug);
        }


		if (MACHINE_IS_PRISM) {
			//Reset to failsafe
			menu_add_item_menu(array_menu_debug,"Reset to Failsafe mode",MENU_OPCION_NORMAL,menu_debug_prism_failsafe,NULL);

		}

		if (MACHINE_IS_Z88 || MACHINE_IS_ZXUNO || MACHINE_IS_PRISM || MACHINE_IS_TBBLUE || superupgrade_enabled.v || MACHINE_IS_TSCONF || MACHINE_IS_BASECONF) {
            menu_add_item_menu(array_menu_debug,"~~Hard Reset",MENU_OPCION_NORMAL,menu_debug_hard_reset,NULL);
			menu_add_item_menu_shortcut(array_menu_debug,'h');
            menu_add_item_menu_tooltip(array_menu_debug,"Hard resets the machine");
            menu_add_item_menu_ayuda(array_menu_debug,"Hard resets the machine.\n"
				"On Z88, it's the same as opening flap and pressing reset button.\n"
				"On ZX-Uno, it's the same as pressing Ctrl-Alt-Backspace or powering off and on the machine"
				);
            menu_add_item_menu_genera_ventana(array_menu_debug);
		}

		if (!CPU_IS_MOTOROLA) {
    		menu_add_item_menu(array_menu_debug,"Generate ~~NMI",MENU_OPCION_NORMAL,menu_debug_nmi,NULL);
            menu_add_item_menu_spanish(array_menu_debug,"Generar ~~NMI");
			menu_add_item_menu_shortcut(array_menu_debug,'n');
            menu_add_item_menu_genera_ventana(array_menu_debug);

			if (MACHINE_IS_TBBLUE && multiface_enabled.v && (tbblue_registers[6]&8) ) {
                menu_add_item_menu(array_menu_debug,"Generate Multiface NMI",MENU_OPCION_NORMAL,menu_debug_nmi_multiface_tbblue,NULL);
                menu_add_item_menu_genera_ventana(array_menu_debug);
            }
		}

		if (MACHINE_IS_ZXUNO_BOOTM_DISABLED) {
            menu_add_item_menu_en_es_ca(array_menu_debug,MENU_OPCION_NORMAL,menu_debug_special_nmi,NULL,
                "Generate Special NMI","Generar NMI Especial","Generar NMI Especial");
            menu_add_item_menu_es_avanzado(array_menu_debug);
		}

		menu_add_item_menu(array_menu_debug,"~~Debug CPU",MENU_OPCION_NORMAL,menu_debug_registers,NULL);
		menu_add_item_menu_shortcut(array_menu_debug,'d');
        menu_add_item_menu_genera_ventana(array_menu_debug);
        //No activamos salir_todos_menus o se saldria de debug cpu, porque se detecta dicha variable
        //menu_add_item_menu_se_cerrara(array_menu_debug);
		menu_add_item_menu_tooltip(array_menu_debug,"Open debug window");
		menu_add_item_menu_ayuda(array_menu_debug,"This window opens the debugger. You can see there some Z80 registers "
					"easily recognizable. Some other variables and entries need further explanation:\n"
					"TSTATES: T-states total in a frame\n"
					"TSTATL: T-states total in a scanline\n"
					"TSTATP: T-states partial. This is a counter that you can reset with key P"



					);


        if (MACHINE_IS_SPECTRUM || MACHINE_IS_CPC) {
            if (util_textadventure_is_daad_quill_paws_gac() ) {
                menu_add_item_menu(array_menu_debug,"Debug Adventure",MENU_OPCION_NORMAL,menu_debug_registers_view_adventure,NULL);
                //No activamos salir_todos_menus o se saldria de debug cpu, porque se detecta dicha variable
                //menu_add_item_menu_se_cerrara(array_menu_debug);

		        menu_add_item_menu_tooltip(array_menu_debug,"Open debug window on the adventure view (view number 8)");
                menu_add_item_menu_ayuda(array_menu_debug,"Open debug window on the adventure view (view number 8)");
                menu_add_item_menu_genera_ventana(array_menu_debug);
            }
        }

        menu_add_item_menu_en_es_ca(array_menu_debug,MENU_OPCION_NORMAL,menu_debug_machine_info,NULL,
            "Machine Info","Información Máquina","Informació màquina");
        menu_add_item_menu_genera_ventana(array_menu_debug);
        menu_add_item_menu_se_cerrara(array_menu_debug);


        menu_add_item_menu(array_menu_debug,"",MENU_OPCION_SEPARADOR,NULL,NULL);
        menu_add_item_menu_es_avanzado(array_menu_debug);

        menu_add_item_menu_en_es_ca(array_menu_debug,MENU_OPCION_NORMAL,menu_debug_load_source_code,NULL,
            "Load Source Code","Cargar Código Fuente","Carregar Codi Font");
        menu_add_item_menu_se_cerrara(array_menu_debug);
        menu_add_item_menu_genera_ventana(array_menu_debug);
        menu_add_item_menu_tooltip(array_menu_debug,"Load source code");
        menu_add_item_menu_ayuda(array_menu_debug,"Load source code. It is shown on the Debug CPU window. "
            "Source code must have labels in format LXXXX or XXXX where XXXX is an address in hexadecimal. \n"
            "If you compile with sjasmplus, you can generate an assembler output file with addresses on every line "
            "so it can be loaded in ZEsarUX. Run it the following way:\n"
            "sjasmplus inputfile.asm --lst=output_extended.asm\n"
            "That output_extended.asm will have labels/addresses on every line. Then go to Debug settings, and uncheck "
            "'Source code L Prefix'. You need also to adjust 'Source code skip cols' to discard X characters from the beginning "
            "of the line until the label/address is located, typically 6 or 7 characters");
        menu_add_item_menu_es_avanzado(array_menu_debug);

        if (remote_tamanyo_archivo_raw_source_code) {
            menu_add_item_menu_en_es_ca(array_menu_debug,MENU_OPCION_NORMAL,menu_debug_unload_source_code,NULL,
                "Unload Source Code","Descartar Codigo Fuente","Descartar Codi Font");
            menu_add_item_menu_se_cerrara(array_menu_debug);
            menu_add_item_menu_es_avanzado(array_menu_debug);
        }

        menu_add_item_menu_en_es_ca(array_menu_debug,MENU_OPCION_NORMAL,menu_debug_load_symbol_table,NULL,
            "Load Symbol Table","Cargar Tabla Símbolos","Carregar Taula Símbols");
        menu_add_item_menu_se_cerrara(array_menu_debug);
        menu_add_item_menu_genera_ventana(array_menu_debug);
        menu_add_item_menu_tooltip(array_menu_debug,"Load symbol table");
        menu_add_item_menu_ayuda(array_menu_debug,"Load symbol table. Format for every line is:\n"
            "LABEL[spaces, tabs, :]VALUE");
        menu_add_item_menu_es_avanzado(array_menu_debug);

        if (parse_string_labeltree!=NULL) {
            menu_add_item_menu_en_es_ca(array_menu_debug,MENU_OPCION_NORMAL,menu_debug_show_symbol_table,NULL,
                "Show symbol table","Mostrar Tabla Símbolos","Mostrar Taula Símbols");
            menu_add_item_menu_se_cerrara(array_menu_debug);
            menu_add_item_menu_genera_ventana(array_menu_debug);
            menu_add_item_menu_es_avanzado(array_menu_debug);

            menu_add_item_menu_en_es_ca(array_menu_debug,MENU_OPCION_NORMAL,menu_debug_symbol_table_unload,NULL,
                "Unload Symbol Table","Descartar Tabla Símbolos","Descartar Taula Símbols");
            menu_add_item_menu_se_cerrara(array_menu_debug);
            menu_add_item_menu_genera_ventana(array_menu_debug);
            menu_add_item_menu_es_avanzado(array_menu_debug);

        }

		if (MACHINE_IS_TSCONF || MACHINE_IS_ZXUNO || datagear_dma_emulation.v) {
			menu_add_item_menu_format(array_menu_debug,MENU_OPCION_NORMAL,menu_debug_dma,NULL,"Debug D~~MA");
			menu_add_item_menu_shortcut(array_menu_debug,'m');
            menu_add_item_menu_se_cerrara(array_menu_debug);
            menu_add_item_menu_genera_ventana(array_menu_debug);
            menu_add_item_menu_es_avanzado(array_menu_debug);
		}

		if (CPU_IS_Z80) {
			menu_add_item_menu_format(array_menu_debug,MENU_OPCION_NORMAL,menu_debug_ioports,NULL,"Debug ~~I/O Ports");
			menu_add_item_menu_shortcut(array_menu_debug,'i');
            menu_add_item_menu_se_cerrara(array_menu_debug);
            menu_add_item_menu_genera_ventana(array_menu_debug);
            menu_add_item_menu_es_avanzado(array_menu_debug);

        }



		if (CPU_IS_MOTOROLA) {
			menu_add_item_menu_format(array_menu_debug,MENU_OPCION_NORMAL,menu_debug_ioports,NULL,"Debug ~~I/O Addresses");
			menu_add_item_menu_shortcut(array_menu_debug,'i');
            menu_add_item_menu_se_cerrara(array_menu_debug);
            menu_add_item_menu_genera_ventana(array_menu_debug);
            menu_add_item_menu_es_avanzado(array_menu_debug);
        }

		if (MACHINE_IS_TSCONF || MACHINE_IS_TBBLUE || MACHINE_IS_CPC) {
			if (MACHINE_IS_TSCONF) {
                menu_add_item_menu_format(array_menu_debug,MENU_OPCION_NORMAL,menu_debug_tsconf_tbblue_msx,NULL,"~~TSConf");
                menu_add_item_menu_shortcut(array_menu_debug,'t');
                menu_add_item_menu_tiene_submenu(array_menu_debug);
            }
			if (MACHINE_IS_TBBLUE) {
                menu_add_item_menu_format(array_menu_debug,MENU_OPCION_NORMAL,menu_debug_tsconf_tbblue_msx,NULL,"Spectrum Nex~~t");
                menu_add_item_menu_shortcut(array_menu_debug,'t');
                menu_add_item_menu_tiene_submenu(array_menu_debug);
            }
            if (MACHINE_IS_CPC) {
                menu_add_item_menu_format(array_menu_debug,MENU_OPCION_NORMAL,menu_debug_tsconf_tbblue_msx,NULL,"CPC");
                menu_add_item_menu_tiene_submenu(array_menu_debug);
            }

		}

		if (MACHINE_HAS_VDP_9918A) {
			char buffer_item[32];

			strcpy(buffer_item,"~~MSX");
			// por defecto


			if (MACHINE_IS_COLECO) {
				strcpy(buffer_item,"Colecovision");
			}

			if (MACHINE_IS_SG1000) {
				strcpy(buffer_item,"SG-1000");
			}

			if (MACHINE_IS_SMS) {
				strcpy(buffer_item,"Master System");
			}

			if (MACHINE_IS_SVI) {
				strcpy(buffer_item,"Spectravideo");
			}


			menu_add_item_menu_format(array_menu_debug,MENU_OPCION_NORMAL,menu_debug_tsconf_tbblue_msx,NULL,buffer_item);

			if (MACHINE_IS_MSX) {
				//Letra M tambien se usa en DMA, pero como no hay DMA en MSX, pues no estaran nunca las dos opciones a la vez
				menu_add_item_menu_shortcut(array_menu_debug,'m');
			}

            menu_add_item_menu_tiene_submenu(array_menu_debug);
		}

        menu_add_item_menu_en_es_ca(array_menu_debug,MENU_OPCION_NORMAL,menu_debug_unnamed_console,NULL,
            "Debug console","Consola Depuración","Consola Depuració");
        menu_add_item_menu_se_cerrara(array_menu_debug);
        menu_add_item_menu_genera_ventana(array_menu_debug);
        menu_add_item_menu_es_avanzado(array_menu_debug);

        menu_add_item_menu(array_menu_debug,"",MENU_OPCION_SEPARADOR,NULL,NULL);




		menu_add_item_menu(array_menu_debug,"He~~xadecimal Editor",MENU_OPCION_NORMAL,menu_debug_hexdump,NULL);
        menu_add_item_menu_spanish(array_menu_debug,"Editor He~~xadecimal");
		menu_add_item_menu_shortcut(array_menu_debug,'x');
        menu_add_item_menu_se_cerrara(array_menu_debug);
        menu_add_item_menu_genera_ventana(array_menu_debug);




        menu_add_item_menu_en_es_ca(array_menu_debug,MENU_OPCION_NORMAL,menu_debug_view_sensors,NULL,
            "View Sensors","Ver Sensores","Veure Sensors");
        menu_add_item_menu_es_avanzado(array_menu_debug);
        menu_add_item_menu_se_cerrara(array_menu_debug);
        menu_add_item_menu_genera_ventana(array_menu_debug);

		if (si_complete_video_driver() ) {
			menu_add_item_menu_en_es_ca(array_menu_debug,MENU_OPCION_NORMAL,menu_debug_view_sprites,NULL,
                "View ~~Sprites","Ver ~~Sprites","Veure ~~Sprites");
			menu_add_item_menu_shortcut(array_menu_debug,'s');
            menu_add_item_menu_se_cerrara(array_menu_debug);
            menu_add_item_menu_genera_ventana(array_menu_debug);
		}



#ifdef EMULATE_VISUALMEM
			//if (!CPU_IS_MOTOROLA) {
			//menu_add_item_menu(array_menu_debug,"Old visual memory",MENU_OPCION_NORMAL,menu_debug_visualmem,NULL);
	                //menu_add_item_menu_tooltip(array_menu_debug,"Show which memory zones are changed or which memory address with opcodes have been executed");
	                //menu_add_item_menu_ayuda(array_menu_debug,"Show which memory zones are changed or which memory address with opcodes have been executed");

			menu_add_item_menu(array_menu_debug,"~~Visual memory",MENU_OPCION_NORMAL,menu_debug_new_visualmem,NULL);
            menu_add_item_menu_spanish(array_menu_debug,"Memoria ~~Visual");
			menu_add_item_menu_shortcut(array_menu_debug,'v');
            menu_add_item_menu_se_cerrara(array_menu_debug);
            menu_add_item_menu_genera_ventana(array_menu_debug);
	                menu_add_item_menu_tooltip(array_menu_debug,"Show which memory zones are changed or which memory address with opcodes have been executed");
	                menu_add_item_menu_ayuda(array_menu_debug,"Show which memory zones are changed or which memory address with opcodes have been executed");
			//}
#endif





    menu_add_item_menu(array_menu_debug,"",MENU_OPCION_SEPARADOR,NULL,NULL);









		menu_add_item_menu_en_es_ca(array_menu_debug,MENU_OPCION_NORMAL,menu_debug_load_binary,NULL,
            "L~~oad binary block","Cargar bl~~oque binario","Carregar bl~~oc binari");
		menu_add_item_menu_shortcut(array_menu_debug,'o');
        menu_add_item_menu_se_cerrara(array_menu_debug);
        menu_add_item_menu_genera_ventana(array_menu_debug);

		menu_add_item_menu_en_es_ca(array_menu_debug,MENU_OPCION_NORMAL,menu_debug_save_binary,NULL,
            "S~~ave binary block","S~~alvar bloque binario","S~~alvar bloc binari");
		menu_add_item_menu_shortcut(array_menu_debug,'a');
        menu_add_item_menu_se_cerrara(array_menu_debug);
        menu_add_item_menu_genera_ventana(array_menu_debug);


		if (menu_desactivado_file_utilities.v==0) {

			menu_add_item_menu_en_es_ca(array_menu_debug,MENU_OPCION_NORMAL,menu_debug_file_utils,NULL,
                "File ~~utilities","~~Utilidades de archivos","~~Utilitats d'arxius");
			menu_add_item_menu_shortcut(array_menu_debug,'u');
            menu_add_item_menu_se_cerrara(array_menu_debug);
            menu_add_item_menu_genera_ventana(array_menu_debug);
			menu_add_item_menu_tooltip(array_menu_debug,"Some file utilities. NOTE: Shortcuts must be chosen pressing Shift+Key");
			menu_add_item_menu_ayuda(array_menu_debug,"Some file utilities.\nNOTE: Shortcuts in file utilities must be chosen by pressing Shift+Key, "
								"I mean, shortcuts are in capital letters to differentiate from quick selecting a file, so for example, "
								"to view a file you must press Shift+v");
            menu_add_item_menu_es_avanzado(array_menu_debug);

		}






		/*
		if (menu_allow_background_windows) {
			menu_add_item_menu_format(array_menu_debug,MENU_OPCION_NORMAL,menu_draw_background_windows,NULL,"ZXVision background windows");
		}
		*/


		menu_add_item_menu_en_es_ca(array_menu_debug,MENU_OPCION_NORMAL,menu_write_message,NULL,
            "Write message","Escribir mensaje","Escriure missatge");
        menu_add_item_menu_se_cerrara(array_menu_debug);
        menu_add_item_menu_genera_ventana(array_menu_debug);
		menu_add_item_menu_tooltip(array_menu_debug,"Just lets you write text in a window, useful if you want to record the display and you want to say something");
		menu_add_item_menu_ayuda(array_menu_debug,"Just lets you write text in a window, useful if you want to record the display and you want to say something");
        menu_add_item_menu_es_avanzado(array_menu_debug);

		menu_add_item_menu_en_es_ca(array_menu_debug,MENU_OPCION_NORMAL,menu_shortcuts_window,NULL,
            "Shortcuts helper","Ayudante de atajos","Ajudant de dreceres");
        menu_add_item_menu_se_cerrara(array_menu_debug);
        menu_add_item_menu_genera_ventana(array_menu_debug);
		menu_add_item_menu_tooltip(array_menu_debug,"Window to see all shortcuts (hotkeys) pressed");
        menu_add_item_menu_ayuda(array_menu_debug,"Window to see all shortcuts (hotkeys) pressed");
        menu_add_item_menu_es_avanzado(array_menu_debug);

		menu_add_item_menu_en_es_ca(array_menu_debug,MENU_OPCION_NORMAL,menu_ascii_table,NULL,
            "Ascii Table","Tabla Ascii","Taula Ascii");
        menu_add_item_menu_se_cerrara(array_menu_debug);
        menu_add_item_menu_genera_ventana(array_menu_debug);




#ifdef TIMESENSORS_ENABLED

		menu_add_item_menu_format(array_menu_debug,MENU_OPCION_NORMAL,menu_debug_timesensors_enable,NULL,"[%c] Timesensors",
		(timesensors_started ? 'X' : ' ' ));

		if (timesensors_started) {
			menu_add_item_menu_format(array_menu_debug,MENU_OPCION_NORMAL,menu_debug_timesensors,NULL,"    List Timesensors");
            menu_add_item_menu_se_cerrara(array_menu_debug);
            menu_add_item_menu_genera_ventana(array_menu_debug);

			menu_add_item_menu_format(array_menu_debug,MENU_OPCION_NORMAL,menu_debug_timesensors_init,NULL,"    Init Timesensors");
            menu_add_item_menu_se_cerrara(array_menu_debug);
		}

#endif



	//testeo
	//menu_add_item_menu_format(array_menu_debug,MENU_OPCION_NORMAL,menu_testeo_scanf_numero,NULL,"Test scanf number");

		/* De momento desactivado
		if (MACHINE_IS_SPECTRUM) {
			menu_add_item_menu_format(array_menu_debug,MENU_OPCION_NORMAL,menu_debug_spritefinder,NULL,"Spritefinder: %s",
					(spritefinder_enabled.v ? "Yes" : "No") );
		}
		*/

        menu_add_item_menu(array_menu_debug,"",MENU_OPCION_SEPARADOR,NULL,NULL);

		menu_add_item_menu_en_es_ca(array_menu_debug,MENU_OPCION_NORMAL,menu_debug_view_basic,menu_debug_view_basic_cond,
            "~~Basic","~~Basic","~~Basic");
		menu_add_item_menu_shortcut(array_menu_debug,'b');
        menu_add_item_menu_tiene_submenu(array_menu_debug);

        if (CPU_IS_Z80) {
			menu_add_item_menu_en_es_ca(array_menu_debug,MENU_OPCION_NORMAL,menu_cpu_transaction_log,NULL,
                "~~CPU Transaction Log","Registro transacciones ~~CPU","Registre transaccions ~~CPU");
			menu_add_item_menu_shortcut(array_menu_debug,'c');
            menu_add_item_menu_tiene_submenu(array_menu_debug);
            menu_add_item_menu_es_avanzado(array_menu_debug);
		}

#ifdef EMULATE_CPU_STATS
		if (CPU_IS_Z80) {
			menu_add_item_menu_en_es_ca(array_menu_debug,MENU_OPCION_NORMAL,menu_debug_cpu_stats,NULL,
                "CPU Statistics","Estadísticas CPU","Estadístiques CPU");
            menu_add_item_menu_tiene_submenu(array_menu_debug);
            menu_add_item_menu_es_avanzado(array_menu_debug);
		}

#endif

        menu_add_item_menu_format(array_menu_debug,MENU_OPCION_NORMAL,menu_find,NULL,"~~Find");
            menu_add_item_menu_shortcut(array_menu_debug,'f');
        menu_add_item_menu_tooltip(array_menu_debug,"Find bytes on memory");
        menu_add_item_menu_ayuda(array_menu_debug,"Find bytes on the 64 KB of mapped memory");
        menu_add_item_menu_tiene_submenu(array_menu_debug);


		menu_add_item_menu(array_menu_debug,"~~Poke",MENU_OPCION_NORMAL,menu_poke,NULL);
		menu_add_item_menu_shortcut(array_menu_debug,'p');
		menu_add_item_menu_tooltip(array_menu_debug,"Poke address manually or from .POK file");
		menu_add_item_menu_ayuda(array_menu_debug,"Poke address for infinite lives, etc...");
        menu_add_item_menu_tiene_submenu(array_menu_debug);


		if (!CPU_IS_MOTOROLA) {
		    menu_add_item_menu_format(array_menu_debug,MENU_OPCION_NORMAL,menu_debug_input_file_keyboard,NULL,"Input File Spoolin~~g");
		    menu_add_item_menu_shortcut(array_menu_debug,'g');
            menu_add_item_menu_tooltip(array_menu_debug,"Sends every character from a text file as keyboard presses");
            menu_add_item_menu_ayuda(array_menu_debug,"Every character from a text file is sent as keyboard presses. Only Ascii characters, not UFT, Unicode or others. "
                                                                   "Symbols that require extended mode on Spectrum are not sent: [ ] (c) ~ \\ { }. These can be used "
                                                                   "as a delay.\n"
								"Note: symbol | means Shift+1 (Edit)");
            menu_add_item_menu_tiene_submenu(array_menu_debug);
            menu_add_item_menu_es_avanzado(array_menu_debug);
		}


        menu_add_item_menu(array_menu_debug,"",MENU_OPCION_SEPARADOR,NULL,NULL);
                //menu_add_item_menu(array_menu_debug,"ESC Back",MENU_OPCION_NORMAL|MENU_OPCION_ESC,NULL,NULL);
		menu_add_ESC_item(array_menu_debug);

        menu_add_item_menu_index_full_path(array_menu_debug,"Main Menu-> Debug","Menú Principal-> Debug","Menú Principal-> Debug");

        retorno_menu=menu_dibuja_menu(&debug_opcion_seleccionada,&item_seleccionado,array_menu_debug,"Debug Menu","Menú Debug","Menú Debug" );



		if ((item_seleccionado.tipo_opcion&MENU_OPCION_ESC)==0 && retorno_menu>=0) {
                        //llamamos por valor de funcion
                        if (item_seleccionado.menu_funcion!=NULL) {
                                //printf ("actuamos por funcion\n");
                                item_seleccionado.menu_funcion(item_seleccionado.valor_opcion);

                        }
                }

	} while ( (item_seleccionado.tipo_opcion&MENU_OPCION_ESC)==0 && retorno_menu!=MENU_RETORNO_ESC && !salir_todos_menus);

}


void menu_snapshot_load(MENU_ITEM_PARAMETERS)
{

        char *filtros[17];

        filtros[0]="zx";
        filtros[1]="sp";
        filtros[2]="z80";
        filtros[3]="sna";
        filtros[4]="o";
        filtros[5]="p";

        filtros[6]="80";
        filtros[7]="81";
        filtros[8]="p81";
        filtros[9]="z81";
        filtros[10]="ace";
		filtros[11]="rzx";
		filtros[12]="zsf";
		filtros[13]="spg";
		filtros[14]="nex";
        filtros[15]="snx";
        filtros[16]=0;



        //guardamos directorio actual
        char directorio_actual[PATH_MAX];
        getcwd(directorio_actual,PATH_MAX);

        //Obtenemos directorio de snap
        //si no hay directorio, vamos a rutas predefinidas
        if (snapfile==NULL) menu_chdir_sharedfiles();

	else {
	        char directorio[PATH_MAX];
	        util_get_dir(snapfile,directorio);
	        //printf ("strlen directorio: %d directorio: %s\n",strlen(directorio),directorio);

	        //cambiamos a ese directorio, siempre que no sea nulo
	        if (directorio[0]!=0) {
	                debug_printf (VERBOSE_INFO,"Changing to last directory: %s",directorio);
	                zvfs_chdir(directorio);
        	}
	}


	int ret;

	ret=menu_filesel("Select Snapshot",filtros,snapshot_load_file);
        //volvemos a directorio inicial
        zvfs_chdir(directorio_actual);


        if (ret==1) {
		snapfile=snapshot_load_file;

		//sin overlay de texto, que queremos ver las franjas de carga con el color normal (no apagado)
		//reset_menu_overlay_function();


			snapshot_load();

		//restauramos modo normal de texto de menu
		//set_menu_overlay_function(normal_overlay_texto_menu);

		//Y salimos de todos los menus
		salir_todos_menus=1;
        }


}

void menu_snapshot_save(MENU_ITEM_PARAMETERS)
{

    menu_first_aid("snapshot_save_zsf");

    char *filtros[6];

  	if (MACHINE_IS_ZX8081) {
        filtros[0]="zsf";

		if (MACHINE_IS_ZX80_TYPE) {
            filtros[1]="o";
            filtros[2]="z81";
            filtros[3]="zx";
		    filtros[4]=0;
        }
		else {
            filtros[1]="p";
            filtros[2]="p81";
            filtros[3]="z81";
		    filtros[4]="zx";
		    filtros[5]=0;
        }
}

	else if (MACHINE_IS_Z88) {
		filtros[0]="zsf";
        filtros[1]="zx";
		filtros[2]=0;
	}

	else if (MACHINE_IS_SPECTRUM_16_48) {
		filtros[0]="zsf";
		filtros[1]="zx";
		filtros[2]="z80";
		filtros[3]="sp";
		filtros[4]="sna";
		filtros[5]=0;
	}

	else if (MACHINE_IS_ACE) {
        filtros[0]="zsf";
		filtros[1]="ace";
        filtros[2]="zx";
		filtros[3]=0;
	}

	else if (MACHINE_IS_CPC) {
        filtros[0]="zsf";
		filtros[1]="zx";
		filtros[2]=0;
    }


	else {
		filtros[0]="zsf";
		filtros[1]="zx";
		filtros[2]="z80";
		filtros[3]="sna";
		filtros[4]=0;
	}


        //guardamos directorio actual
        char directorio_actual[PATH_MAX];
        getcwd(directorio_actual,PATH_MAX);


 		//Obtenemos directorio de snap save
        //si no hay directorio, vamos a rutas predefinidas
        if (snapshot_save_file[0]==0) menu_chdir_sharedfiles();

		else {
			char directorio[PATH_MAX];
			util_get_dir(snapshot_save_file,directorio);
			//printf ("strlen directorio: %d directorio: %s\n",strlen(directorio),directorio);

			//cambiamos a ese directorio, siempre que no sea nulo
			if (directorio[0]!=0) {
					debug_printf (VERBOSE_INFO,"Changing to last directory: %s",directorio);
					zvfs_chdir(directorio);
			}
		}



		int ret;

		ret=menu_filesel_save("Snapshot file",filtros,snapshot_save_file);
        //volvemos a directorio inicial
        zvfs_chdir(directorio_actual);

        if (ret==1) {

			//Ver si archivo existe y preguntar
			struct stat buf_stat;

			if (stat(snapshot_save_file, &buf_stat)==0) {

			if (menu_confirm_yesno_texto("File exists","Overwrite?")==0) return;

		}


		snapshot_save(snapshot_save_file);

		//Si ha ido bien la grabacion
		if (!if_pending_error_message) menu_generic_message_splash("Save Snapshot","OK. Snapshot saved");

			//Y salimos de todos los menus
			salir_todos_menus=1;

        }




}




void menu_snapshot_stop_rzx_play(MENU_ITEM_PARAMETERS)
{
	eject_rzx_file();
}


//int menu_snapshot_quicksave_contador_archivo=1;

void menu_snapshot_quicksave(MENU_ITEM_PARAMETERS)
{
	char final_name[PATH_MAX];
	snapshot_quick_save(final_name);

    //Crear un icono siempre que zx desktop este visible
    zxvision_create_configurable_icon_file_type(F_FUNCION_DESKTOP_SNAPSHOT,final_name);


	menu_generic_message_format("Quicksave","OK. Snapshot name: %s",final_name);

}


void menu_snapshot_quickload(MENU_ITEM_PARAMETERS)
{

	char *filtros[2];

	filtros[0]="autosnap";
	filtros[1]=0;



	//guardamos directorio actual
	char directorio_actual[PATH_MAX];
	getcwd(directorio_actual,PATH_MAX);

	//Vamos a directorio de autosnap
	zvfs_chdir(snapshot_autosave_interval_quicksave_directory);


	int ret;

	ret=menu_filesel("Select Snapshot",filtros,snapshot_load_file);
	//volvemos a directorio inicial
	zvfs_chdir(directorio_actual);


	if (ret==1) {
		snapfile=snapshot_load_file;

		//sin overlay de texto, que queremos ver las franjas de carga con el color normal (no apagado)
		//reset_menu_overlay_function();


		snapshot_load();

		//restauramos modo normal de texto de menu
		//set_menu_overlay_function(normal_overlay_texto_menu);

		//Y salimos de todos los menus
		salir_todos_menus=1;
	}


}


void menu_snapshot_save_game_config(MENU_ITEM_PARAMETERS)
{

        char *filtros[30];

        filtros[0]="zx";
        filtros[1]="sp";
        filtros[2]="z80";
        filtros[3]="sna";

        filtros[4]="o";
        filtros[5]="p";
        filtros[6]="80";
        filtros[7]="81";
        filtros[8]="z81";

        filtros[9]="tzx";
        filtros[10]="tap";

        filtros[11]="rwa";
        filtros[12]="smp";
        filtros[13]="wav";

        filtros[14]="epr";
        filtros[15]="63";
        filtros[16]="eprom";
        filtros[17]="flash";

        filtros[18]="ace";

        filtros[19]="dck";

        filtros[20]="cdt";

        filtros[21]="ay";

        filtros[22]="scr";

        filtros[23]="rzx";

        filtros[24]="zsf";

        filtros[25]="spg";

        filtros[26]="trd";

        filtros[27]="config";

        filtros[28]="p81";

        filtros[29]=0;

	char source_file[PATH_MAX];
	char game_config_file[PATH_MAX];


	//por defecto
	source_file[0]=0;

        //guardamos directorio actual
        char directorio_actual[PATH_MAX];
        getcwd(directorio_actual,PATH_MAX);

	int ret;

        //Obtenemos directorio de quickload, para generar el .config en la ultima ruta que se haya hecho smartload
        //si no hay directorio, vamos a rutas predefinidas
        if (quickfile!=NULL)  {
			debug_printf (VERBOSE_INFO,"Last smartload file: %s",quickfile);

			char nombre[PATH_MAX];
			util_get_file_no_directory(quickfile,nombre);


			int usar_nombre_autodetectado;



			//Si nombre vacio, no usar nombre autodetectado
			if (nombre[0]==0) {
				usar_nombre_autodetectado=0;
			}

			else {
				//Nombre previo. El usuario quiere usarlo?
				char nombre_shown[25];
				menu_tape_settings_trunc_name(nombre,nombre_shown,25);
				usar_nombre_autodetectado=menu_confirm_yesno_texto("Generate config for",nombre_shown);

			}

			if (usar_nombre_autodetectado) {
				strcpy(source_file,quickfile);
				ret=1;
			}


			else {
				//No seleccionamos ultimo quickfile como source. Cambiar a directorio quickfile
	              char directorio[PATH_MAX];
        	        util_get_dir(quickfile,directorio);
                	//printf ("strlen directorio: %d directorio: %s\n",strlen(directorio),directorio);

	                //cambiamos a ese directorio, siempre que no sea nulo
        	        if (directorio[0]!=0) {
                	        debug_printf (VERBOSE_INFO,"Changing to last directory: %s",directorio);
                        	zvfs_chdir(directorio);
	        }


		}

    }

	//Si no se selecciona source_file como ultimo archivo cargado
	if (source_file[0]==0) {

			ret=menu_filesel("Source or dest file",filtros,source_file);
			//volvemos a directorio inicial
			zvfs_chdir(directorio_actual);

	}

	debug_printf (VERBOSE_INFO,"Source file: %s",source_file);

    if (ret) {

		//Archivo final agregar .config, si es que no es ya el .config el que hemos seleccionado
		if (!util_compare_file_extension(source_file,"config")) strcpy(game_config_file,source_file);
		else sprintf (game_config_file,"%s.config",source_file);


		debug_printf (VERBOSE_INFO,"Destination file will be %s",game_config_file);

		 //Ver si archivo existe y preguntar
                if (si_existe_archivo(game_config_file) ) {

                        if (menu_confirm_yesno_texto("File exists","Overwrite?")==0) return;

                }



		util_save_game_config(game_config_file);
		menu_generic_message_splash("Save autoconfig","OK. File saved");


    }

}


void menu_snapshot_add_note(MENU_ITEM_PARAMETERS)
{

    menu_ventana_scanf("Snapshot note",snap_zsf_note_save,SNAP_ZSF_NOTE_LENGTH);

}

void menu_snapshot_view_loaded_note(MENU_ITEM_PARAMETERS)
{
    menu_generic_message("Loaded note",snap_zsf_note_loaded);
}

void menu_snapshot(MENU_ITEM_PARAMETERS)
{

    menu_item *array_menu_snapshot;
    menu_item item_seleccionado;
    int retorno_menu;

    do {


        menu_add_item_menu_inicial(&array_menu_snapshot,"~~Load snapshot",MENU_OPCION_NORMAL,menu_snapshot_load,NULL);
        menu_add_item_menu_spanish_catalan(array_menu_snapshot,"~~Load instantánea","~~Load instantània");
        menu_add_item_menu_genera_ventana(array_menu_snapshot);
        menu_add_item_menu_shortcut(array_menu_snapshot,'l');
        menu_add_item_menu_tooltip(array_menu_snapshot,"Load snapshot");
        menu_add_item_menu_ayuda(array_menu_snapshot,"Supported snapshot formats on load:\n"
                "Z80, ZX, SP, SNA, O, 80, P, 81, Z81");

        menu_add_item_menu_en_es_ca(array_menu_snapshot,MENU_OPCION_NORMAL,menu_snapshot_save,NULL,
            "~~Save snapshot","~~Salvar instantánea","~~Salvar instantània");
        menu_add_item_menu_shortcut(array_menu_snapshot,'s');
        menu_add_item_menu_genera_ventana(array_menu_snapshot);
        menu_add_item_menu_tooltip(array_menu_snapshot,"Save snapshot of the current machine state");
        menu_add_item_menu_ayuda(array_menu_snapshot,"Supported snapshot formats on save:\n"
                "Z80, ZX, SP, P, O\n"
                "You must write the file name with the extension");

        menu_add_item_menu(array_menu_snapshot,"",MENU_OPCION_SEPARADOR,NULL,NULL);

        menu_add_item_menu_en_es_ca(array_menu_snapshot,MENU_OPCION_NORMAL,menu_snapshot_add_note,NULL,
            "Add note to next ZSF snapshot","Agregar nota a siguiente snapshot ZSF","Agregar nota a següent snapshot ZSF");
        menu_add_item_menu_tooltip(array_menu_snapshot,"Next saved ZSF snapshot will have this text note");
        menu_add_item_menu_ayuda(array_menu_snapshot,"Next saved ZSF snapshot will have this text note");
        menu_add_item_menu_es_avanzado(array_menu_snapshot);
        menu_add_item_menu_genera_ventana(array_menu_snapshot);
        menu_add_item_menu_se_cerrara(array_menu_snapshot);

        if (snap_zsf_note_loaded[0]) {
            menu_add_item_menu_en_es_ca(array_menu_snapshot,MENU_OPCION_NORMAL,menu_snapshot_view_loaded_note,NULL,
                "View loaded ZSF note","Ver nota del ZSF cargado","Veure nota del ZSF carregat");
            menu_add_item_menu_es_avanzado(array_menu_snapshot);
            menu_add_item_menu_genera_ventana(array_menu_snapshot);
            menu_add_item_menu_se_cerrara(array_menu_snapshot);
        }

        menu_add_item_menu(array_menu_snapshot,"",MENU_OPCION_SEPARADOR,NULL,NULL);
        menu_add_item_menu_es_avanzado(array_menu_snapshot);

        if (rzx_reproduciendo) {
            menu_add_item_menu_en_es_ca(array_menu_snapshot,MENU_OPCION_NORMAL,menu_snapshot_stop_rzx_play,NULL,
                "Stop RZX Play","Detener RZX Play","Aturar RZX Play");
            menu_add_item_menu(array_menu_snapshot,"",MENU_OPCION_SEPARADOR,NULL,NULL);
        }


        menu_add_item_menu_en_es_ca(array_menu_snapshot,MENU_OPCION_NORMAL,menu_snapshot_quickload,NULL,
            "Quickl~~oad","L~~oad rápido","L~~oad ràpid");
        menu_add_item_menu_shortcut(array_menu_snapshot,'o');
        menu_add_item_menu_add_flags(array_menu_snapshot,MENU_ITEM_FLAG_GENERA_VENTANA | MENU_ITEM_FLAG_SE_CERRARA);
        menu_add_item_menu_tooltip(array_menu_snapshot,"Load a snapshot quickly");
        menu_add_item_menu_ayuda(array_menu_snapshot,"Browses on the quicksave directory");

        menu_add_item_menu_en_es_ca(array_menu_snapshot,MENU_OPCION_NORMAL,menu_snapshot_quicksave,NULL,
            "Quicks~~ave","S~~alvado rápido","S~~alvat ràpid");
        menu_add_item_menu_se_cerrara(array_menu_snapshot);
        menu_add_item_menu_genera_ventana(array_menu_snapshot);
        menu_add_item_menu_shortcut(array_menu_snapshot,'a');
        menu_add_item_menu_tooltip(array_menu_snapshot,"Save a snapshot quickly");
        menu_add_item_menu_ayuda(array_menu_snapshot,"Save a snapshot quickly. Name prefix and directory to save are configured on settings->Snapshot");




        //Este separador solo aparece cuando la opcion avanzada de autoconfig tambien aparece, por tanto, es un item avanzado en si mismo
        menu_add_item_menu(array_menu_snapshot,"",MENU_OPCION_SEPARADOR,NULL,NULL);
        menu_add_item_menu_es_avanzado(array_menu_snapshot);

        menu_add_item_menu_en_es_ca(array_menu_snapshot,MENU_OPCION_NORMAL,menu_snapshot_save_game_config,NULL,
            "Save a~~utoconfig file","Salvar archivo a~~utoconfig","Salvar arxiu a~~utoconfig");
        menu_add_item_menu_shortcut(array_menu_snapshot,'u');
        menu_add_item_menu_tooltip(array_menu_snapshot,"Generate .config file with common settings");
        menu_add_item_menu_ayuda(array_menu_snapshot,"Generate .config file with common settings. Used to define custom settings for games, "
            "by default it asks to generate a .config file for the last smartloaded game");
        menu_add_item_menu_es_avanzado(array_menu_snapshot);
        menu_add_item_menu_se_cerrara(array_menu_snapshot);
        menu_add_item_menu_genera_ventana(array_menu_snapshot);


        menu_add_item_menu(array_menu_snapshot,"",MENU_OPCION_SEPARADOR,NULL,NULL);

        menu_add_item_menu_en_es_ca(array_menu_snapshot,MENU_OPCION_NORMAL,menu_snapshot_rewind,NULL,
            "Snapshots to ~~RAM","Instantáneas a ~~RAM","Instantànies a ~~RAM");
        menu_add_item_menu_shortcut(array_menu_snapshot,'r');
        menu_add_item_menu_tooltip(array_menu_snapshot,"Options to keep last snapshots in RAM");
        menu_add_item_menu_ayuda(array_menu_snapshot,"Options to keep last snapshots in RAM");
        menu_add_item_menu_tiene_submenu(array_menu_snapshot);


        menu_add_item_menu(array_menu_snapshot,"",MENU_OPCION_SEPARADOR,NULL,NULL);
        menu_add_ESC_item(array_menu_snapshot);

        //La llamada a menu_add_item_menu_index_full_path lo agrega al principio del array, por tanto lo podemos llamar desde cualquier
        //punto de la creacion del menu. Lo llamo desde aqui porque queda esteticamente mas bonito
        menu_add_item_menu_index_full_path(array_menu_snapshot,"Main Menu-> Snapshot","Menú Principal-> Instantánea","Menú Principal-> Instantània");

        retorno_menu=menu_dibuja_menu(&snapshot_opcion_seleccionada,&item_seleccionado,array_menu_snapshot,"Snapshot Menu","Menú Instantánea","Menú Instantània");



        if ((item_seleccionado.tipo_opcion&MENU_OPCION_ESC)==0 && retorno_menu>=0) {
            //llamamos por valor de funcion
            if (item_seleccionado.menu_funcion!=NULL) {
                //printf ("actuamos por funcion\n");
                item_seleccionado.menu_funcion(item_seleccionado.valor_opcion);

            }
        }

    } while ( (item_seleccionado.tipo_opcion&MENU_OPCION_ESC)==0 && retorno_menu!=MENU_RETORNO_ESC && !salir_todos_menus);




}






//Comun para coleco y msx y sg1000 y sms
void menu_msxcart_load(MENU_ITEM_PARAMETERS)
{

        char *filtros[5];

		if (MACHINE_IS_COLECO) {
        	filtros[0]="col";
			filtros[1]=0;
		}

		else if (MACHINE_IS_SG1000) {
        	filtros[0]="sg";
			//Aunque extensión SC es de la sega sc3000, algunos cartuchos medio funcionan
			filtros[1]="sc";
			filtros[2]=0;
		}

		else if (MACHINE_IS_SMS) {
            filtros[0]="sms";
        	filtros[1]="sg";
			//Aunque extensión SC es de la sega sc3000, algunos cartuchos medio funcionan
			filtros[2]="sc";
            filtros[3]="bin";
			filtros[4]=0;
		}

		else {
			filtros[0]="rom";
			filtros[1]=0;
		}





        //guardamos directorio actual
        char directorio_actual[PATH_MAX];
        getcwd(directorio_actual,PATH_MAX);

        //Obtenemos directorio de ultimo archivo
        //si no hay directorio, vamos a rutas predefinidas
        if (last_msx_cart[0]==0) menu_chdir_sharedfiles();

        else {
                char directorio[PATH_MAX];
                util_get_dir(last_msx_cart,directorio);
                //printf ("strlen directorio: %d directorio: %s\n",strlen(directorio),directorio);

                //cambiamos a ese directorio, siempre que no sea nulo
                if (directorio[0]!=0) {
                        debug_printf (VERBOSE_INFO,"Changing to last directory: %s",directorio);
                        zvfs_chdir(directorio);
                }
        }


        int ret;

        ret=menu_filesel("Select Cartridge",filtros,last_msx_cart);
        //volvemos a directorio inicial
		zvfs_chdir(directorio_actual);


        if (ret==1) {
		//                sprintf (last_msx_cart,"%s",msxcart_load_file);

                //sin overlay de texto, que queremos ver las franjas de carga con el color normal (no apagado)
                //reset_menu_overlay_function();


				if (MACHINE_IS_MSX) {
                        msx_insert_rom_cartridge(last_msx_cart);
				}
				else if (MACHINE_IS_SG1000) {
					sg1000_insert_rom_cartridge(last_msx_cart);
				}

				else if (MACHINE_IS_SMS) {
					sms_insert_rom_cartridge(last_msx_cart);
				}

				else if (MACHINE_IS_SVI) {
					svi_insert_rom_cartridge(last_msx_cart);
				}

				else {
					coleco_insert_rom_cartridge(last_msx_cart);
				}

                //restauramos modo normal de texto de menu
                //set_menu_overlay_function(normal_overlay_texto_menu);

                //Y salimos de todos los menus
                salir_todos_menus=1;
        }


}


void menu_msxcart_eject(MENU_ITEM_PARAMETERS)
{

	if (MACHINE_IS_COLECO) coleco_empty_romcartridge_space();
	else if (MACHINE_IS_SG1000) sg1000_empty_romcartridge_space();
    else if (MACHINE_IS_SMS) sms_empty_romcartridge_space();
	else if (MACHINE_IS_SVI) svi_empty_romcartridge_space();
	else msx_empty_romcartridge_space();
	menu_generic_message("Eject Cartridge","OK. Cartridge ejected");
}

void menu_sms_mapper_type(MENU_ITEM_PARAMETERS)
{
    sms_mapper_type++;

    //Si es mayor que el ultimo, resetear
    if (sms_mapper_type>SMS_MAPPER_TYPE_CODEMASTERS) sms_mapper_type=SMS_MAPPER_TYPE_NONE;
}

void menu_sms_mapper_cartridge_size(MENU_ITEM_PARAMETERS)
{
    sms_cartridge_size *=2;

    if (sms_cartridge_size>SMS_MAX_ROM_SIZE) sms_cartridge_size=8192;

    sms_set_mapper_mask_bits();

}

void menu_msx_mapper_type(MENU_ITEM_PARAMETERS)
{
    msx_mapper_type++;

    if (msx_mapper_type>MSX_MAPPER_TYPE_RTYPE) msx_mapper_type=MSX_MAPPER_TYPE_NONE;

    reset_cpu();

}

void menu_msxcart(MENU_ITEM_PARAMETERS)
{

        menu_item *array_menu_msxcart;
        menu_item item_seleccionado;
        int retorno_menu;

        do {


                menu_add_item_menu_inicial(&array_menu_msxcart,"~~Load Cartridge",MENU_OPCION_NORMAL,menu_msxcart_load,NULL);
                menu_add_item_menu_shortcut(array_menu_msxcart,'l');
                menu_add_item_menu_tooltip(array_menu_msxcart,"Load msx Cartridge");
                menu_add_item_menu_ayuda(array_menu_msxcart,"Supported msx cartridge formats on load:\n"
                                        "DCK");

                if (MACHINE_IS_SMS && sms_cartridge_inserted.v) {


                    char buf_mapper_type[33];

                    //Por defecto
                    strcpy(buf_mapper_type,"None");

                    if (sms_mapper_type==SMS_MAPPER_TYPE_SEGA) strcpy(buf_mapper_type,"Sega");
                    if (sms_mapper_type==SMS_MAPPER_TYPE_CODEMASTERS) strcpy(buf_mapper_type,"CodeMasters");

                    menu_add_item_menu_format(array_menu_msxcart,MENU_OPCION_NORMAL,menu_sms_mapper_type,NULL,"Mapper type [%s]",buf_mapper_type);

                    //Esto no tiene mucho sentido cambiarlo a no ser que quieras hacer debug o ver que sucede....
                    menu_add_item_menu_format(array_menu_msxcart,MENU_OPCION_NORMAL,menu_sms_mapper_cartridge_size,NULL,"Cartridge Size [%d KB]",sms_cartridge_size/1024);

                    menu_add_item_menu(array_menu_msxcart,"",MENU_OPCION_SEPARADOR,NULL,NULL);

                }

                if (MACHINE_IS_MSX && msx_cartridge_inserted.v) {


                    char buf_mapper_type[100];

                    //Por defecto
                    strcpy(buf_mapper_type,"None");

                    if (msx_mapper_type==MSX_MAPPER_TYPE_ASCII_16KB) strcpy(buf_mapper_type,"Ascii 16kb");
                    if (msx_mapper_type==MSX_MAPPER_TYPE_ASCII_8KB) strcpy(buf_mapper_type,"Ascii 8kb");
                    if (msx_mapper_type==MSX_MAPPER_TYPE_KONAMI_MEGAROM_WITHOUT_SCC) strcpy(buf_mapper_type,"Konami without SCC");
                    if (msx_mapper_type==MSX_MAPPER_TYPE_KONAMI_MEGAROM_WITH_SCC) strcpy(buf_mapper_type,"Konami with SCC");
                    if (msx_mapper_type==MSX_MAPPER_TYPE_RTYPE) strcpy(buf_mapper_type,"R-Type");


                    menu_add_item_menu_format(array_menu_msxcart,MENU_OPCION_NORMAL,menu_msx_mapper_type,NULL,"[%s] Mapper type",buf_mapper_type);

                    menu_add_item_menu(array_menu_msxcart,"",MENU_OPCION_SEPARADOR,NULL,NULL);
                    menu_add_item_menu_format(array_menu_msxcart,MENU_OPCION_SEPARADOR,NULL,NULL,"---Info---");
                    menu_add_item_menu_format(array_menu_msxcart,MENU_OPCION_SEPARADOR,NULL,NULL,"Cartridge Size: %d KB",msx_cartridge_size/1024);

                    menu_add_item_menu(array_menu_msxcart,"",MENU_OPCION_SEPARADOR,NULL,NULL);

                }

                //en SMS , solo deja eject si hay algo insertado
                //TODO: hacerlo igual en el resto de maquinas?
                if (MACHINE_IS_SMS) {
                    if (sms_cartridge_inserted.v) menu_add_item_menu(array_menu_msxcart,"~~Eject Cartridge",MENU_OPCION_NORMAL,menu_msxcart_eject,NULL);
                }

                else {
                    menu_add_item_menu(array_menu_msxcart,"~~Eject Cartridge",MENU_OPCION_NORMAL,menu_msxcart_eject,NULL);
                }


                menu_add_item_menu_shortcut(array_menu_msxcart,'e');
                menu_add_item_menu_tooltip(array_menu_msxcart,"Eject Cartridge");
                menu_add_item_menu_ayuda(array_menu_msxcart,"Eject Cartridge");


     				menu_add_item_menu(array_menu_msxcart,"",MENU_OPCION_SEPARADOR,NULL,NULL);
                menu_add_ESC_item(array_menu_msxcart);

				char window_title[64];

				if (MACHINE_IS_COLECO) strcpy(window_title,"Coleco Cartridge");
				else if (MACHINE_IS_SG1000) strcpy(window_title,"SG-1000 Cartridge");
                else if (MACHINE_IS_SMS) strcpy(window_title,"SMS Cartridge");
				else if (MACHINE_IS_SVI) strcpy(window_title,"SVI Cartridge");
				else strcpy(window_title,"MSX Cartridge");

                retorno_menu=menu_dibuja_menu_no_title_lang(&msxcart_opcion_seleccionada,&item_seleccionado,array_menu_msxcart,window_title);



                if ((item_seleccionado.tipo_opcion&MENU_OPCION_ESC)==0 && retorno_menu>=0) {
                        //llamamos por valor de funcion
                        if (item_seleccionado.menu_funcion!=NULL) {
                                //printf ("actuamos por funcion\n");
                                item_seleccionado.menu_funcion(item_seleccionado.valor_opcion);

                        }
                }

        } while ( (item_seleccionado.tipo_opcion&MENU_OPCION_ESC)==0 && retorno_menu!=MENU_RETORNO_ESC && !salir_todos_menus);

}




int menu_insert_slot_number;
int menu_insert_slot_ram_size;
int menu_insert_slot_type;
//char menu_insert_slot_rom_name[PATH_MAX];
char menu_insert_slot_eprom_name[PATH_MAX];
char menu_insert_slot_flash_intel_name[PATH_MAX];

void menu_z88_slot_insert_internal_ram(MENU_ITEM_PARAMETERS)
{
    menu_item *array_menu_common;
    menu_item item_seleccionado;
    int retorno_menu;

    //por defecto
    int common_opcion_seleccionada=0;

    //RAM interna. Siempre entre 32 y 512 K

    do {

        menu_add_item_menu_inicial(&array_menu_common,"",MENU_OPCION_UNASSIGNED,NULL,NULL);

        int i;
        int tamanyo=32768;

        for (i=0;i<5;i++) {
            menu_add_item_menu_format(array_menu_common,MENU_OPCION_NORMAL,NULL,NULL,"%d Kb",tamanyo/1024);
            menu_add_item_menu_valor_opcion(array_menu_common,tamanyo);

            //Obtener la posicion segun la ram actual. 0=32kb, 1=64kb, etc
            if (tamanyo==menu_insert_slot_ram_size) common_opcion_seleccionada=i;

            tamanyo *=2;
        }


        retorno_menu=menu_dibuja_menu_no_title_lang(&common_opcion_seleccionada,&item_seleccionado,array_menu_common,"RAM Size");

        if ((item_seleccionado.tipo_opcion&MENU_OPCION_ESC)==0 && retorno_menu>=0) {
            menu_insert_slot_ram_size=item_seleccionado.valor_opcion;
            return;
        }

    } while ( (item_seleccionado.tipo_opcion&MENU_OPCION_ESC)==0 && retorno_menu!=MENU_RETORNO_ESC && !salir_todos_menus);


}

void menu_z88_slot_insert_ram(MENU_ITEM_PARAMETERS)
{

    menu_item *array_menu_common;
    menu_item item_seleccionado;
    int retorno_menu;

    //por defecto
    int common_opcion_seleccionada=0;


    do {

        menu_add_item_menu_inicial(&array_menu_common,"",MENU_OPCION_UNASSIGNED,NULL,NULL);

        int i;
        int tamanyo=32768;

        for (i=0;i<6;i++) {
            menu_add_item_menu_format(array_menu_common,MENU_OPCION_NORMAL,NULL,NULL,"%d Kb",tamanyo/1024);
            menu_add_item_menu_valor_opcion(array_menu_common,tamanyo);

            //Obtener la posicion segun la ram actual. 0=32kb, 1=64kb, etc
            if (tamanyo==menu_insert_slot_ram_size) common_opcion_seleccionada=i;

            tamanyo *=2;
        }


        retorno_menu=menu_dibuja_menu_no_title_lang(&common_opcion_seleccionada,&item_seleccionado,array_menu_common,"Size");

        if ((item_seleccionado.tipo_opcion&MENU_OPCION_ESC)==0 && retorno_menu>=0) {
            menu_insert_slot_ram_size=item_seleccionado.valor_opcion;
            return;
        }

    } while ( (item_seleccionado.tipo_opcion&MENU_OPCION_ESC)==0 && retorno_menu!=MENU_RETORNO_ESC && !salir_todos_menus);


}


int menu_z88_eprom_size(MENU_ITEM_PARAMETERS)
{
    menu_item *array_menu_z88_eprom_size;
    menu_item item_seleccionado;
    int retorno_menu;

    do {
        //32, 128, 256

        menu_add_item_menu_inicial_format(&array_menu_z88_eprom_size,MENU_OPCION_NORMAL,NULL,NULL,"32 Kb");

        menu_add_item_menu_format(array_menu_z88_eprom_size,MENU_OPCION_NORMAL,NULL,NULL,"128 Kb");

        menu_add_item_menu_format(array_menu_z88_eprom_size,MENU_OPCION_NORMAL,NULL,NULL,"256 Kb");


        menu_add_item_menu(array_menu_z88_eprom_size,"",MENU_OPCION_SEPARADOR,NULL,NULL);
        menu_add_ESC_item(array_menu_z88_eprom_size);

        retorno_menu=menu_dibuja_menu_no_title_lang(&z88_eprom_size_opcion_seleccionada,&item_seleccionado,array_menu_z88_eprom_size,"Eprom Size" );



        if ((item_seleccionado.tipo_opcion&MENU_OPCION_ESC)==0 && retorno_menu>=0) {
            //llamamos por valor de funcion
            if (item_seleccionado.menu_funcion!=NULL) {
                //printf ("actuamos por funcion\n");
                item_seleccionado.menu_funcion(item_seleccionado.valor_opcion);

            }

            //Devolver tamanyo eprom
            if (z88_eprom_size_opcion_seleccionada==0) return 32*1024;
            if (z88_eprom_size_opcion_seleccionada==1) return 128*1024;
            if (z88_eprom_size_opcion_seleccionada==2) return 256*1024;
        }

    } while ( (item_seleccionado.tipo_opcion&MENU_OPCION_ESC)==0 && retorno_menu!=MENU_RETORNO_ESC && !salir_todos_menus);

	//Salimos con ESC, devolver 0
	return 0;
}


void menu_z88_slot_insert_eprom(MENU_ITEM_PARAMETERS)
{
    char *filtros[4];

    filtros[0]="eprom";
    filtros[1]="epr";
    filtros[2]="63";
    filtros[3]=0;

//guardamos directorio actual
    char directorio_actual[PATH_MAX];
    getcwd(directorio_actual,PATH_MAX);

    //Obtenemos directorio de rom
    //si no hay directorio, vamos a rutas predefinidas
    if (menu_insert_slot_eprom_name[0]==0) menu_chdir_sharedfiles();
    else {
        char directorio[PATH_MAX];
        util_get_dir(menu_insert_slot_eprom_name,directorio);
        //printf ("strlen directorio: %d directorio: %s\n",strlen(directorio),directorio);

        //cambiamos a ese directorio, siempre que no sea nulo
        if (directorio[0]!=0) {
                debug_printf (VERBOSE_INFO,"Changing to last directory: %s",directorio);
                zvfs_chdir(directorio);
        }
    }



    int ret;

    ret=menu_filesel("Select existing or new",filtros,menu_insert_slot_eprom_name);
    //volvemos a directorio inicial
    zvfs_chdir(directorio_actual);

    if (ret==1) {

        //Ver si archivo existe y preguntar si inicializar o no
        struct stat buf_stat;

        if (stat(menu_insert_slot_eprom_name, &buf_stat)!=0) {

            if (menu_confirm_yesno_texto("File does not exist","Create?")==0) {
                menu_insert_slot_eprom_name[0]=0;
                return;
            }

            //Preguntar tamanyo EPROM
            int size=menu_z88_eprom_size(0);

            if (size==0) {
                //ha salido con ESC
                menu_insert_slot_eprom_name[0]=0;
                return;
            }


            if (z88_create_blank_eprom_flash_file(menu_insert_slot_eprom_name,size)) {
                menu_insert_slot_eprom_name[0]=0;
            }

            return;
        }
    }

    else menu_insert_slot_eprom_name[0]=0;
}


void menu_z88_slot_insert_hybrid_eprom(MENU_ITEM_PARAMETERS)
{
    char *filtros[4];

    filtros[0]="eprom";
    filtros[1]="epr";
    //filtros[2]="63"; .63 extensions no admitidas para eprom que son hybridas
    filtros[2]=0;

    //guardamos directorio actual
    char directorio_actual[PATH_MAX];
    getcwd(directorio_actual,PATH_MAX);

    //Obtenemos directorio de rom
    //si no hay directorio, vamos a rutas predefinidas
    if (menu_insert_slot_eprom_name[0]==0) menu_chdir_sharedfiles();
    else {
        char directorio[PATH_MAX];
        util_get_dir(menu_insert_slot_eprom_name,directorio);
        //printf ("strlen directorio: %d directorio: %s\n",strlen(directorio),directorio);

        //cambiamos a ese directorio, siempre que no sea nulo
        if (directorio[0]!=0) {
                debug_printf (VERBOSE_INFO,"Changing to last directory: %s",directorio);
                zvfs_chdir(directorio);
        }
    }



    int ret;

    ret=menu_filesel("Select existing or new",filtros,menu_insert_slot_eprom_name);
    //volvemos a directorio inicial
    zvfs_chdir(directorio_actual);

    if (ret==1) {

        //Ver si archivo existe y preguntar si inicializar o no
        struct stat buf_stat;

        if (stat(menu_insert_slot_eprom_name, &buf_stat)!=0) {

            if (menu_confirm_yesno_texto("File does not exist","Create?")==0) {
                    menu_insert_slot_eprom_name[0]=0;
                    return;
            }

            //Tamanyo de la parte EPROM es de 512kb
            int size=512*1024;


            if (z88_create_blank_eprom_flash_file(menu_insert_slot_eprom_name,size)) {
                menu_insert_slot_eprom_name[0]=0;
            }

            return;
        }
    }

    else menu_insert_slot_eprom_name[0]=0;
}



int menu_z88_flash_intel_size(MENU_ITEM_PARAMETERS)
{
    menu_item *array_menu_z88_flash_intel_size;
    menu_item item_seleccionado;
    int retorno_menu;

    do {
        //512,1024

        menu_add_item_menu_inicial_format(&array_menu_z88_flash_intel_size,MENU_OPCION_NORMAL,NULL,NULL,"512 Kb");

        menu_add_item_menu_format(array_menu_z88_flash_intel_size,MENU_OPCION_NORMAL,NULL,NULL,"1 Mb");


        menu_add_item_menu(array_menu_z88_flash_intel_size,"",MENU_OPCION_SEPARADOR,NULL,NULL);
        menu_add_ESC_item(array_menu_z88_flash_intel_size);

        retorno_menu=menu_dibuja_menu_no_title_lang(&z88_flash_intel_size_opcion_seleccionada,&item_seleccionado,array_menu_z88_flash_intel_size,"Flash Size" );



        if ((item_seleccionado.tipo_opcion&MENU_OPCION_ESC)==0 && retorno_menu>=0) {
            //llamamos por valor de funcion
            if (item_seleccionado.menu_funcion!=NULL) {
                //printf ("actuamos por funcion\n");
                item_seleccionado.menu_funcion(item_seleccionado.valor_opcion);

            }

            //Devolver tamanyo flash_intel
            if (z88_flash_intel_size_opcion_seleccionada==0) return 512*1024;
            if (z88_flash_intel_size_opcion_seleccionada==1) return 1024*1024;
        }

    } while ( (item_seleccionado.tipo_opcion&MENU_OPCION_ESC)==0 && retorno_menu!=MENU_RETORNO_ESC && !salir_todos_menus);

    //Salimos con ESC, devolver 0
    return 0;
}


void menu_z88_slot_insert_flash_intel(MENU_ITEM_PARAMETERS)
{
    char *filtros[2];

    filtros[0]="flash";
    filtros[1]=0;

    int ret;

    ret=menu_filesel("Select existing or new",filtros,menu_insert_slot_flash_intel_name);

    if (ret==1) {

        //Ver si archivo existe y preguntar si inicializar o no
        struct stat buf_stat;

        if (stat(menu_insert_slot_flash_intel_name, &buf_stat)!=0) {

            if (menu_confirm_yesno_texto("File does not exist","Create?")==0) {
                    menu_insert_slot_flash_intel_name[0]=0;
                    return;
            }

            //Preguntar tamanyo Flash
            int size=menu_z88_flash_intel_size(0);

            if (size==0) {
                //ha salido con ESC
                menu_insert_slot_flash_intel_name[0]=0;
                return;
            }


            if (z88_create_blank_eprom_flash_file(menu_insert_slot_flash_intel_name,size)) {
                menu_insert_slot_flash_intel_name[0]=0;
            }

            return;
        }
    }

    else menu_insert_slot_flash_intel_name[0]=0;
}



//Si se han aplicado cambios (insertar tarjeta memoria) para volver a menu anterior
//z80_bit menu_z88_slot_insert_applied_changes={0};

void menu_z88_slot_insert_apply(MENU_ITEM_PARAMETERS)
{

	if (menu_insert_slot_number==0) {
		//Memoria interna

		if (menu_confirm_yesno("Need Hard Reset")==1) {
			z88_change_internal_ram(menu_insert_slot_ram_size);
			//menu_z88_slot_insert_applied_changes.v=1;
            menu_generic_message_splash("Apply changes","OK. Changes applied");
		}
		return;
	}


	if (menu_insert_slot_type==0) {
		//RAM
		z88_insert_ram_card(menu_insert_slot_ram_size,menu_insert_slot_number);
		//menu_z88_slot_insert_applied_changes.v=1;
        menu_generic_message_splash("Apply changes","OK. Changes applied");
	}



    if (menu_insert_slot_type==2) {
        //EPROM
        if (menu_insert_slot_eprom_name[0]==0) debug_printf (VERBOSE_ERR,"Empty EPROM name");

        else {
            z88_load_eprom_card(menu_insert_slot_eprom_name,menu_insert_slot_number);
            //menu_z88_slot_insert_applied_changes.v=1;
            menu_generic_message_splash("Apply changes","OK. Changes applied");
        }
    }

    if (menu_insert_slot_type==3) {
        //Intel Flash
        if (menu_insert_slot_flash_intel_name[0]==0) debug_printf (VERBOSE_ERR,"Empty Flash name");

        else {
            z88_load_flash_intel_card(menu_insert_slot_flash_intel_name,menu_insert_slot_number);
            //menu_z88_slot_insert_applied_changes.v=1;
            menu_generic_message_splash("Apply changes","OK. Changes applied");
        }
    }


    if (menu_insert_slot_type==4) {
        //Hybrida RAM+EPROM
        if (menu_insert_slot_eprom_name[0]==0) debug_printf (VERBOSE_ERR,"Empty EPROM name");

        else {
            z88_load_hybrid_eprom_card(menu_insert_slot_eprom_name,menu_insert_slot_number);
            //menu_z88_slot_insert_applied_changes.v=1;
            menu_generic_message_splash("Apply changes","OK. Changes applied");
        }
    }


}




void menu_z88_slot_insert_change_type(MENU_ITEM_PARAMETERS)
{

    menu_espera_no_tecla();


    menu_item *array_menu_common;
    menu_item item_seleccionado;
    int retorno_menu;

    //Indicamos la opcion segun el tipo
    int common_opcion_seleccionada;

    if (menu_insert_slot_ram_size==0) common_opcion_seleccionada=0;
    else common_opcion_seleccionada=menu_insert_slot_type+1;

    //tipo 1 rom lo saltamos, no usado. que seria la posicion 2: 0:empty, 1:ram, 2:rom, 3:eprom...
    if (common_opcion_seleccionada>=2) common_opcion_seleccionada--;



    do {

        menu_add_item_menu_inicial_format(&array_menu_common,MENU_OPCION_NORMAL,NULL,NULL,"Empty");

        int i;

        for (i=0;i<5;i++) {

            //tipo 1 rom no usado
            if (i!=1) {

                menu_add_item_menu_format(array_menu_common,MENU_OPCION_NORMAL,NULL,NULL,z88_memory_types[i]);
                menu_add_item_menu_valor_opcion(array_menu_common,i);
            }

        }


        retorno_menu=menu_dibuja_menu_no_title_lang(&common_opcion_seleccionada,&item_seleccionado,array_menu_common,"Slot type");



        if ((item_seleccionado.tipo_opcion&MENU_OPCION_ESC)==0 && retorno_menu>=0) {
            //llamamos por valor de funcion
            //En vez de eso simplemente cambiamos valores
            if (common_opcion_seleccionada==0) {
                //pasamos a vacio - ram con 0 kb
                menu_insert_slot_type=0;
                menu_insert_slot_ram_size=0;
            }
            else {
                if (menu_insert_slot_ram_size==0) menu_insert_slot_ram_size=32768;
                menu_insert_slot_type=item_seleccionado.valor_opcion;
            }
            return;
        }

    } while ( (item_seleccionado.tipo_opcion&MENU_OPCION_ESC)==0 && retorno_menu!=MENU_RETORNO_ESC && !salir_todos_menus);

//no cambiar nada


}



//valor_opcion le viene dado desde la propia funcion de gestion de menu
void menu_z88_slot_insert(MENU_ITEM_PARAMETERS)
{
	//menu_insert_slot_number=z88_slots_opcion_seleccionada;


	menu_insert_slot_number=valor_opcion;

	debug_printf (VERBOSE_DEBUG,"Slot selected on menu: %d",menu_insert_slot_number);

	if (menu_insert_slot_number<0 || menu_insert_slot_number>3) cpu_panic ("Invalid slot number");

	//menu_z88_slot_insert_applied_changes.v=0;


	if (menu_insert_slot_number>0) {
		//Memoria externa
		//guardamos tamanyo +1
		menu_insert_slot_ram_size=z88_memory_slots[menu_insert_slot_number].size+1;

		if (menu_insert_slot_ram_size==1) menu_insert_slot_ram_size=0;

		menu_insert_slot_type=z88_memory_slots[menu_insert_slot_number].type;
	}

	else {
		//Memoria interna
		//guardamos tamanyo +1

		menu_insert_slot_ram_size=z88_internal_ram_size+1;
	}



	//reseteamos nombre rom
	//menu_insert_slot_rom_name[0]=0;

	//copiamos nombre eprom/flash a las dos variables
	strcpy(menu_insert_slot_eprom_name,z88_memory_slots[menu_insert_slot_number].eprom_flash_nombre_archivo);

	strcpy(menu_insert_slot_flash_intel_name,z88_memory_slots[menu_insert_slot_number].eprom_flash_nombre_archivo);




    menu_item *array_menu_z88_slot_insert;
    menu_item item_seleccionado;
    int retorno_menu;
    //char string_slot_name_shown[20];
	char string_slot_eprom_name_shown[30],string_slot_flash_intel_name_shown[30];
	char string_memory_type[20];

    //Esta opcion siempre local aqui y empezar en la 0
    int z88_slot_insert_opcion_seleccionada=0;

    do {


		menu_tape_settings_trunc_name(menu_insert_slot_eprom_name,string_slot_eprom_name_shown,30);
		menu_tape_settings_trunc_name(menu_insert_slot_flash_intel_name,string_slot_flash_intel_name_shown,30);



		if (menu_insert_slot_ram_size==0) sprintf (string_memory_type,"%s","Empty");
		else sprintf (string_memory_type,"%s",z88_memory_types[menu_insert_slot_type]);

		//Memorias externas

		if (menu_insert_slot_number>0) {

			menu_add_item_menu_inicial_format(&array_menu_z88_slot_insert,MENU_OPCION_NORMAL,menu_z88_slot_insert_change_type,NULL,"Memory type: %s",string_memory_type);
            menu_add_item_menu_tooltip(array_menu_z88_slot_insert,"Type of memory card if present");
            menu_add_item_menu_ayuda(array_menu_z88_slot_insert,"Type of memory card if present");

			if (menu_insert_slot_type==0 && menu_insert_slot_ram_size!=0) {
				//RAM
				menu_add_item_menu_format(array_menu_z88_slot_insert,MENU_OPCION_NORMAL,menu_z88_slot_insert_ram,NULL,"Size: %d Kb",(menu_insert_slot_ram_size)/1024);
                menu_add_item_menu_tooltip(array_menu_z88_slot_insert,"Size of RAM card");
                menu_add_item_menu_ayuda(array_menu_z88_slot_insert,"Size of RAM card");

			}

			if (menu_insert_slot_type==1) {
				cpu_panic("ROM cards do not exist on Z88");

			}

			if (menu_insert_slot_type==2) {
                //EPROM
                menu_add_item_menu_format(array_menu_z88_slot_insert,MENU_OPCION_NORMAL,menu_z88_slot_insert_eprom,NULL,"Name: %s",string_slot_eprom_name_shown);
                menu_add_item_menu_tooltip(array_menu_z88_slot_insert,"EPROM file to use");
                menu_add_item_menu_ayuda(array_menu_z88_slot_insert,"EPROM file to use. Valid formats are .eprom. Select existing or new");

            }

            if (menu_insert_slot_type==3) {
                //Intel Flash
                menu_add_item_menu_format(array_menu_z88_slot_insert,MENU_OPCION_NORMAL,menu_z88_slot_insert_flash_intel,NULL,"Name: %s",string_slot_flash_intel_name_shown);
                menu_add_item_menu_tooltip(array_menu_z88_slot_insert,"Intel Flash file to use");
                menu_add_item_menu_ayuda(array_menu_z88_slot_insert,"Intel Flash file to use. Valid formats are .flash. Select existing or new");
            }

			if (menu_insert_slot_type==4) {
                //Hybrida RAM+EPROM
                menu_add_item_menu_format(array_menu_z88_slot_insert,MENU_OPCION_NORMAL,menu_z88_slot_insert_hybrid_eprom,NULL,"Name: %s",string_slot_eprom_name_shown);
                menu_add_item_menu_tooltip(array_menu_z88_slot_insert,"Hybrid RAM+EPROM file to use");
                menu_add_item_menu_ayuda(array_menu_z88_slot_insert,"Hybrid RAM+EPROM file to use. Valid formats are .eprom. Select existing or new");
            }



		}

		else {
			//Memoria interna
			menu_add_item_menu_inicial_format(&array_menu_z88_slot_insert,MENU_OPCION_NORMAL,menu_z88_slot_insert_internal_ram,NULL,"RAM Size: %d Kb",(menu_insert_slot_ram_size)/1024);
			menu_add_item_menu_tooltip(array_menu_z88_slot_insert,"Size of RAM card");
            menu_add_item_menu_ayuda(array_menu_z88_slot_insert,"Size of RAM card");
		}




		menu_add_item_menu_format(array_menu_z88_slot_insert,MENU_OPCION_NORMAL,menu_z88_slot_insert_apply,NULL,"Apply changes");
        menu_add_item_menu_tooltip(array_menu_z88_slot_insert,"Apply slot changes");
        menu_add_item_menu_ayuda(array_menu_z88_slot_insert,"Apply slot changes");





        menu_add_item_menu(array_menu_z88_slot_insert,"",MENU_OPCION_SEPARADOR,NULL,NULL);


        menu_add_ESC_item(array_menu_z88_slot_insert);

		//Titulo de la ventana variable segun el nombre del slot
		char texto_titulo[32];
		sprintf (texto_titulo,"Modify Slot %d",menu_insert_slot_number);

        retorno_menu=menu_dibuja_menu_no_title_lang(&z88_slot_insert_opcion_seleccionada,&item_seleccionado,array_menu_z88_slot_insert,texto_titulo);



        if ((item_seleccionado.tipo_opcion&MENU_OPCION_ESC)==0 && retorno_menu>=0) {
            //llamamos por valor de funcion
            if (item_seleccionado.menu_funcion!=NULL) {
                //printf ("actuamos por funcion\n");
                item_seleccionado.menu_funcion(item_seleccionado.valor_opcion);

            }
        }

    } while ( (item_seleccionado.tipo_opcion&MENU_OPCION_ESC)==0 && retorno_menu!=MENU_RETORNO_ESC && !salir_todos_menus /*&& !menu_z88_slot_insert_applied_changes.v */);


}

void menu_z88_slot_erase_eprom_flash(MENU_ITEM_PARAMETERS)
{
        if (menu_confirm_yesno("Erase Card")==1) {
		z88_erase_eprom_flash();
		menu_generic_message("Erase Card","OK. Card erased");
        }
}

void menu_z88_eprom_flash_reclaim_free_space(MENU_ITEM_PARAMETERS)
{
       if (menu_confirm_yesno("Reclaim Free Space")==1) {
                z80_long_int liberados=z88_eprom_flash_reclaim_free_space();
                menu_generic_message_format("Reclaim Free Space","OK. %d bytes reclaimed",liberados);
        }

}

void menu_z88_eprom_flash_undelete_files(MENU_ITEM_PARAMETERS)
{
       if (menu_confirm_yesno("Undelete Files")==1) {
                z80_long_int undeleted=z88_eprom_flash_recover_deleted();
                menu_generic_message_format("Undelete Files","OK. %d files undeleted",undeleted);
        }

}

char copy_to_eprom_nombrearchivo[PATH_MAX]="";

void menu_z88_slot_copy_to_eprom_flash(MENU_ITEM_PARAMETERS)
{

	//printf ("%d\n",sizeof(copy_to_eprom_nombrearchivo));

        char *filtros[2];

        filtros[0]="";
        filtros[1]=0;


        //guardamos directorio actual
        char directorio_actual[PATH_MAX];
        getcwd(directorio_actual,PATH_MAX);

        int ret;


        //Obtenemos ultimo directorio visitado
        if (copy_to_eprom_nombrearchivo[0]!=0) {
                char directorio[PATH_MAX];
                util_get_dir(copy_to_eprom_nombrearchivo,directorio);
                //printf ("strlen directorio: %d directorio: %s\n",strlen(directorio),directorio);

                //cambiamos a ese directorio, siempre que no sea nulo
                if (directorio[0]!=0) {
                        debug_printf (VERBOSE_INFO,"Changing to last directory: %s",directorio);
                        zvfs_chdir(directorio);
                }
        }




        ret=menu_filesel("Select file to copy",filtros,copy_to_eprom_nombrearchivo);

        //volvemos a directorio inicial
        zvfs_chdir(directorio_actual);

        if (ret==1) {
		//Cargamos archivo en memoria. Maximo 128 KB
		z80_byte *datos;
		const z80_long_int max_load=131072;
		datos=malloc(max_load);

		FILE *ptr_load;
		ptr_load=fopen(copy_to_eprom_nombrearchivo,"rb");

		if (!ptr_load) {
			debug_printf (VERBOSE_ERR,"Unable to open file %s",copy_to_eprom_nombrearchivo);
			return;
		}


		z80_long_int leidos=fread(datos,1,max_load,ptr_load);
		if (leidos==max_load) {
			debug_printf (VERBOSE_ERR,"Can not load more than %d bytes",max_load);
			return;
		}


		if (leidos<1) {
			debug_printf (VERBOSE_ERR,"No bytes read");
			return;
		}


		//esto puede retornar algun error mediante debug_printf
		//nombre en eprom, quitando carpetas
		char nombre_eprom[256];
		util_get_file_no_directory(copy_to_eprom_nombrearchivo,nombre_eprom);


		//Preguntar nombre en destino. Truncar a maximo de nombre en tarjeta memoria
		nombre_eprom[Z88_MAX_CARD_FILENAME]=0;
		menu_ventana_scanf("Target file name?",nombre_eprom,Z88_MAX_CARD_FILENAME+1);


		if (z88_eprom_flash_fwrite(nombre_eprom,datos,leidos)==0) {
			menu_generic_message("Copy File","OK. File copied to Card");
		}

		free(datos);

	}

}

void menu_z88_slot_card_browser_aux(int slot,char *texto_buffer,int max_texto)
{
        z88_dir dir;
        z88_eprom_flash_file file;

        z88_eprom_flash_find_init(&dir,slot);

        char buffer_nombre[Z88_MAX_CARD_FILENAME+1];

        int retorno;
        int longitud;


        int indice_buffer=0;

        do {
                retorno=z88_eprom_flash_find_next(&dir,&file);
                if (retorno) {
                        z88_eprom_flash_get_file_name(&file,buffer_nombre);

			if (buffer_nombre[0]=='.') buffer_nombre[0]='D'; //archivo borrado

			//printf ("nombre: %s c1: %d\n",buffer_nombre,buffer_nombre[0]);
                        longitud=strlen(buffer_nombre)+1; //Agregar salto de linea
                        if (indice_buffer+longitud>max_texto-1) retorno=0;
                        else {
                                sprintf (&texto_buffer[indice_buffer],"%s\n",buffer_nombre);
                                indice_buffer +=longitud;
                        }

                }
        } while (retorno!=0);

        texto_buffer[indice_buffer]=0;

}

void menu_z88_slot_card_browser(MENU_ITEM_PARAMETERS)
{
#define MAX_TEXTO 4096
        char texto_buffer[MAX_TEXTO];

        int slot=valor_opcion;

        menu_z88_slot_card_browser_aux(slot,texto_buffer,MAX_TEXTO);


	//Si esta vacio
        if (texto_buffer[0]==0) {
                debug_printf(VERBOSE_ERR,"Card is empty");
                return;
        }

        //menu_generic_message_tooltip("Card browser", 0, 0, 1, NULL, "%s", texto_buffer);

	zxvision_generic_message_tooltip("Card Browser" , 1, 0 , 0, 0, 1, NULL, 1, 0, "%s", texto_buffer);

}


void menu_z88_slot_copy_from_eprom(MENU_ITEM_PARAMETERS)
{
#define MAX_TEXTO 4096
	char texto_buffer[MAX_TEXTO];

	int slot=valor_opcion;

	menu_z88_slot_card_browser_aux(slot,texto_buffer,MAX_TEXTO);

	//Si esta vacio
	if (texto_buffer[0]==0) {
		debug_printf(VERBOSE_ERR,"Card is empty");
                return;
        }



	//printf ("archivos: %s\n",texto_buffer);
	generic_message_tooltip_return retorno_archivo;
	//menu_generic_message_tooltip("Select file", 0, 0, 1, &retorno_archivo, "%s", texto_buffer);
	zxvision_generic_message_tooltip("Select file" , 1, 0 , 0, 0, 1, &retorno_archivo, 1,0, "%s", texto_buffer);

	//Si se sale con ESC
	if (retorno_archivo.estado_retorno==0) return;

       //        strcpy(retorno->texto_seleccionado,buffer_lineas[linea_final];
       //         retorno->linea_seleccionada=linea_final;
	//printf ("Seleccionado: (%s) linea: %d\n",retorno_archivo.texto_seleccionado,retorno_archivo.linea_seleccionada);
	//nombre archivo viene con espacio al final


	int longitud_nombre=strlen(retorno_archivo.texto_seleccionado);
	if (longitud_nombre) {
		if (retorno_archivo.texto_seleccionado[longitud_nombre-1] == ' ') {
			retorno_archivo.texto_seleccionado[longitud_nombre-1]=0;
		}
	}



        z88_dir dir;
        z88_eprom_flash_file file;


	z88_find_eprom_flash_file (&dir,&file,retorno_archivo.texto_seleccionado, slot);


	//con el puntero a dir, retornamos file

	//printf ("dir: %x bank: %x\n",dir.dir,dir.bank);

	//z88_return_eprom_flash_file (&dir,&file);

	//Si no es archivo valido
	if (file.namelength==0 || file.namelength==255) {
		debug_printf (VERBOSE_ERR,"File not found");
		return;
	}


	//Grabar archivo en disco. Supuestamente le damos el mismo nombre evitando la / inicial

	//Con selector de archivos
	char nombredestino[PATH_MAX];

	char *filtros[2];

        filtros[0]="";
        filtros[1]=0;


        if (menu_filesel("Select Target File",filtros,nombredestino)==0) return;


	//Ver si archivo existe
	if (si_existe_archivo(nombredestino)) {
		if (menu_confirm_yesno_texto("File exists","Overwrite?")==0) return;
	}




	//Hacer que dir apunte a los datos
	dir.bank=file.datos.bank;
	dir.dir=file.datos.dir;

	z80_byte byte_leido;

                                FILE *ptr_file_save;
                                  ptr_file_save=fopen(nombredestino,"wb");
                                  if (!ptr_file_save)
                                {
                                      debug_printf (VERBOSE_ERR,"Unable to open Binary file %s",nombredestino);

                                  }
                                else {
					unsigned int i;
					z80_long_int size=file.size[0]+(file.size[1]*256)+(file.size[2]*65536)+(file.size[3]*16777216);

					for (i=0;i<size;i++) {
						byte_leido=peek_byte_no_time_z88_bank_no_check_low(dir.dir,dir.bank);
						z88_increment_pointer(&dir);
						fwrite(&byte_leido,1,1,ptr_file_save);
					}


                                  fclose(ptr_file_save);
				}
	menu_generic_message("Copy File","OK. File copied from Card to Disk");




}

void menu_z88_slot_close_flap(MENU_ITEM_PARAMETERS)
{
    z88_close_flap_ahora();
}

void menu_z88_slot_open_flap(MENU_ITEM_PARAMETERS)
{
    z88_open_flap();
}

void menu_z88_slot_persistent_writes(MENU_ITEM_PARAMETERS)
{
    z88_eprom_or_flash_persistent_writes.v ^=1;
}

void menu_z88_slot_card_expand(MENU_ITEM_PARAMETERS)
{
    menu_storage_tape_expand(z88_memory_slots[valor_opcion].eprom_flash_nombre_archivo,NULL);
}

//menu z88 slots
void menu_z88_slots(MENU_ITEM_PARAMETERS)
{
    menu_item *array_menu_z88_slots;
    menu_item item_seleccionado;
    int retorno_menu;

    int slot=valor_opcion;

    //Esta opcion siempre local aqui y empezar en la 0
    int z88_slots_opcion_seleccionada=0;

    do {

        menu_add_item_menu_inicial(&array_menu_z88_slots,"",MENU_OPCION_UNASSIGNED,NULL,NULL);

        if (slot==0) {
            menu_add_item_menu_format(array_menu_z88_slots,MENU_OPCION_NORMAL,menu_z88_slot_insert,NULL,"Change RAM size");
            menu_add_item_menu_tiene_submenu(array_menu_z88_slots);
            menu_add_item_menu_tooltip(array_menu_z88_slots,"Internal ROM and RAM");
            menu_add_item_menu_ayuda(array_menu_z88_slots,"Internal RAM can be maximum 512 KB. Internal ROM can be changed from \n"
                "Machine Menu->Custom Machine, and can also be maximum 512 KB");

            //establecemos numero slot como opcion de ese item de menu
            menu_add_item_menu_valor_opcion(array_menu_z88_slots,0);

            menu_add_item_menu_format(array_menu_z88_slots,MENU_OPCION_SEPARADOR,NULL,NULL," ROM: %d Kb RAM: %d Kb",(z88_internal_rom_size+1)/1024,(z88_internal_ram_size+1)/1024);

        }

        else {


            menu_add_item_menu_format(array_menu_z88_slots,MENU_OPCION_NORMAL,menu_z88_slot_insert,NULL,"Modify slot");
            menu_add_item_menu_tiene_submenu(array_menu_z88_slots);
			//establecemos numero slot como opcion de ese item de menu
			menu_add_item_menu_valor_opcion(array_menu_z88_slots,slot);
            menu_add_item_menu_tooltip(array_menu_z88_slots,"Type of memory card if present and file name in case of slot 3 and EPROM/Flash cards");
            menu_add_item_menu_ayuda(array_menu_z88_slots,"Type of memory card if present and file name in case of slot 3 and EPROM/Flash cards.\n"
                "Slot 1, 2 or 3 can contain "
                "EPROM or Intel Flash cards. But EPROMS and Flash cards can only be written on slot 3\n"
                "EPROM/Flash cards files on slots 1,2 are only used at insert time and loaded on Z88 memory\n"
                "EPROM/Flash cards files on slot 3 are loaded on Z88 memory at insert time but are written everytime \n"
                "a change is made on this Z88 memory, so, they are always used when they are inserted\n"
                "\n"
                "Flash card files and eprom files are internally compatible, but eprom size maximum is 256 Kb "
                "and flash minimum size is 512 Kb, so you can not load an eprom file as a flash or viceversa\n"
                );
                //"Note: if you use OZ rom 4.3.1 or higher, flash cards are recognized well; if you use a lower "
                //"version, flash cards are recognized as eprom cards");

			int eprom_flash_valida=0;
			int tipo_tarjeta=-1;
			char *tipos_tarjeta[]={"Applications","Files","Mixed"};
			int type;

			//Si no hay slot insertado
			if (z88_memory_slots[slot].size==0) {
				menu_add_item_menu_format(array_menu_z88_slots,MENU_OPCION_SEPARADOR,NULL,NULL," Empty");
			}

			else {
				type=z88_memory_slots[slot].type;

				//Si es flash/eprom en slot de escritura(3), indicar a que archivo hace referencia
				if (slot==3 && (type==2 || type==3 || type==4) ) {
					char string_writable_card_shown[28];
					menu_tape_settings_trunc_name(z88_memory_slots[slot].eprom_flash_nombre_archivo,string_writable_card_shown,28);
					menu_add_item_menu_format(array_menu_z88_slots,MENU_OPCION_SEPARADOR,NULL,NULL," %s: %s",z88_memory_types[type],string_writable_card_shown);
				}
				else {
					menu_add_item_menu_format(array_menu_z88_slots,MENU_OPCION_SEPARADOR,NULL,NULL," %s",z88_memory_types[type]);
				}
			}



			if (z88_memory_slots[slot].size!=0) {

				int size=(z88_memory_slots[slot].size+1)/1024;

				//Si es EPROM o Flash, decir espacio libre, detectando si hay una tarjeta con filesystem
				char string_info_tarjeta[100];
				if (type==2 || type==3 || type==4) {
					z80_long_int total_eprom,used_eprom, free_eprom;
					tipo_tarjeta=z88_return_card_type(slot);
					debug_printf (VERBOSE_DEBUG,"Card type: %d",tipo_tarjeta);

					if (tipo_tarjeta>0) {

						//No buscar filesystem en caso de tarjeta hibrida
						if (type==4) {
							sprintf (string_info_tarjeta,"(%s) %d kb Free Unknown",tipos_tarjeta[tipo_tarjeta],size);
                        }

						else {

							z88_eprom_flash_free(&total_eprom,&used_eprom, &free_eprom,slot);

							//Controlar si eprom corrupta, tamanyos logicos
							if (free_eprom>1024*1024) {
								sprintf (string_info_tarjeta,"(%s) %d kb Free Unknown",tipos_tarjeta[tipo_tarjeta],size);
							}

							else {
								sprintf (string_info_tarjeta,"(%s) %d K Free %d K",tipos_tarjeta[tipo_tarjeta],size,free_eprom/1024);
								eprom_flash_valida=1;
							}
						}
					}

					else {
						//0 o -1
						if (tipo_tarjeta==0) {
							sprintf (string_info_tarjeta,"(%s) %d kb",tipos_tarjeta[tipo_tarjeta],size);
						}
						else {
							sprintf (string_info_tarjeta,"(Unknown) %d kb",size);
						}
					}
				}

				else {
					sprintf (string_info_tarjeta,"%d K",size);
				}

				menu_add_item_menu_format(array_menu_z88_slots,MENU_OPCION_SEPARADOR,NULL,NULL," Info: %s",string_info_tarjeta);


			}

			//Si hay una eprom o flash en slot 3, dar opcion de borrar
			if (slot==3 && z88_memory_slots[3].size!=0 && (z88_memory_slots[3].type==2 || z88_memory_slots[3].type==3) ) {
                menu_add_item_menu(array_menu_z88_slots,"",MENU_OPCION_SEPARADOR,NULL,NULL);

                menu_add_item_menu_format(array_menu_z88_slots,MENU_OPCION_NORMAL,menu_z88_slot_persistent_writes,NULL,
                    "[%c] Persistent writes",(z88_eprom_or_flash_persistent_writes.v ? 'X' : ' ') );
                menu_add_item_menu_tooltip(array_menu_z88_slots,"Tells if card writes are saved to disk");
                menu_add_item_menu_ayuda(array_menu_z88_slots,"Tells if card writes are saved to disk. "
                    "Note: all writing operations to card are always saved to internal memory, but this setting "
                    "tells if these changes are written to disk or not.");

				menu_add_item_menu_format(array_menu_z88_slots,MENU_OPCION_NORMAL,menu_z88_slot_erase_eprom_flash,NULL,"Erase Card");
                menu_add_item_menu_prefijo(array_menu_z88_slots,"    ");
                menu_add_item_menu_add_flags(array_menu_z88_slots,MENU_ITEM_FLAG_GENERA_VENTANA | MENU_ITEM_FLAG_SE_CERRARA);
                menu_add_item_menu_tooltip(array_menu_z88_slots,"Card can only be erased on slot 3");
                menu_add_item_menu_ayuda(array_menu_z88_slots,"Card can only be erased on slot 3");
			}


			if (eprom_flash_valida && tipo_tarjeta>0) {

				if (slot==3) {

                    menu_add_item_menu(array_menu_z88_slots,"",MENU_OPCION_SEPARADOR,NULL,NULL);

					menu_add_item_menu_format(array_menu_z88_slots,MENU_OPCION_NORMAL,menu_z88_eprom_flash_reclaim_free_space,NULL,"Reclaim Free Space");
                    menu_add_item_menu_prefijo(array_menu_z88_slots,"    ");
                    menu_add_item_menu_add_flags(array_menu_z88_slots,MENU_ITEM_FLAG_GENERA_VENTANA | MENU_ITEM_FLAG_SE_CERRARA);
					menu_add_item_menu_tooltip(array_menu_z88_slots,"It reclaims the space used by deleted files");
					menu_add_item_menu_ayuda(array_menu_z88_slots,"It reclaims the space used by deleted files");

					menu_add_item_menu_format(array_menu_z88_slots,MENU_OPCION_NORMAL,menu_z88_eprom_flash_undelete_files,NULL,"Undelete Files");
                    menu_add_item_menu_prefijo(array_menu_z88_slots,"    ");
                    menu_add_item_menu_add_flags(array_menu_z88_slots,MENU_ITEM_FLAG_GENERA_VENTANA | MENU_ITEM_FLAG_SE_CERRARA);
					menu_add_item_menu_tooltip(array_menu_z88_slots,"Undelete deleted files");
					menu_add_item_menu_ayuda(array_menu_z88_slots,"Undelete deleted files");


					menu_add_item_menu_format(array_menu_z88_slots,MENU_OPCION_NORMAL,menu_z88_slot_copy_to_eprom_flash,NULL,"Copy to Card");
                    menu_add_item_menu_prefijo(array_menu_z88_slots,"    ");
                    menu_add_item_menu_add_flags(array_menu_z88_slots,MENU_ITEM_FLAG_GENERA_VENTANA | MENU_ITEM_FLAG_SE_CERRARA);
					menu_add_item_menu_tooltip(array_menu_z88_slots,"Copy files from your hard drive to Card");
					menu_add_item_menu_ayuda(array_menu_z88_slots,"Copy files from your hard drive to Card. "
						"Card card must be initialized before you can copy files to it, "
						"I mean, if it's a new card, it must be erased and Z88 Filer must know it; usually it is needed to copy "
						"at least one file from Filer");
				}


                menu_add_item_menu(array_menu_z88_slots,"",MENU_OPCION_SEPARADOR,NULL,NULL);

				menu_add_item_menu_format(array_menu_z88_slots,MENU_OPCION_NORMAL,menu_z88_slot_card_browser,NULL,"Card browser");

                //Solo desplazar a la derecha si ha podido salir setting de persistent writes, para que quede todo bien alineado
                if (slot==3) menu_add_item_menu_prefijo(array_menu_z88_slots,"    ");
                menu_add_item_menu_add_flags(array_menu_z88_slots,MENU_ITEM_FLAG_GENERA_VENTANA | MENU_ITEM_FLAG_SE_CERRARA);
				menu_add_item_menu_tooltip(array_menu_z88_slots,"Browse card");
				menu_add_item_menu_ayuda(array_menu_z88_slots,"Browse card");
                //establecemos numero slot como opcion de ese item de menu
                menu_add_item_menu_valor_opcion(array_menu_z88_slots,slot);


                menu_add_item_menu_en_es_ca(array_menu_z88_slots,MENU_OPCION_NORMAL,menu_z88_slot_card_expand,NULL,
                    "View Expanded","Ver Expandido","Veure Expandit");
                if (slot==3) menu_add_item_menu_prefijo(array_menu_z88_slots,"    ");
                menu_add_item_menu_tooltip(array_menu_z88_slots,"Expand eprom file");
                menu_add_item_menu_ayuda(array_menu_z88_slots,"Expand eprom file");
                menu_add_item_menu_valor_opcion(array_menu_z88_slots,slot);
                menu_add_item_menu_add_flags(array_menu_z88_slots,MENU_ITEM_FLAG_GENERA_VENTANA | MENU_ITEM_FLAG_SE_CERRARA | MENU_ITEM_FLAG_ES_AVANZADO);


				menu_add_item_menu_format(array_menu_z88_slots,MENU_OPCION_NORMAL,menu_z88_slot_copy_from_eprom,NULL,"Copy from Card");

                //Solo desplazar a la derecha si ha podido salir setting de persistent writes, para que quede todo bien alineado
                if (slot==3) menu_add_item_menu_prefijo(array_menu_z88_slots,"    ");
                menu_add_item_menu_add_flags(array_menu_z88_slots,MENU_ITEM_FLAG_GENERA_VENTANA | MENU_ITEM_FLAG_SE_CERRARA);
				menu_add_item_menu_tooltip(array_menu_z88_slots,"Copy files from Card to your hard drive");
				menu_add_item_menu_ayuda(array_menu_z88_slots,"Copy files from Card to your hard drive");
                //establecemos numero slot como opcion de ese item de menu
                menu_add_item_menu_valor_opcion(array_menu_z88_slots,slot);


			}

		}


        menu_add_item_menu(array_menu_z88_slots,"",MENU_OPCION_SEPARADOR,NULL,NULL);





		menu_add_ESC_item(array_menu_z88_slots);

        char titulo_ventana[30];
        sprintf(titulo_ventana,"Z88 Memory Slot %d",slot);

        retorno_menu=menu_dibuja_menu_no_title_lang(&z88_slots_opcion_seleccionada,&item_seleccionado,array_menu_z88_slots,titulo_ventana);



        if ((item_seleccionado.tipo_opcion&MENU_OPCION_ESC)==0 && retorno_menu>=0) {
            //llamamos por valor de funcion
            if (item_seleccionado.menu_funcion!=NULL) {
                //printf ("actuamos por funcion\n");
                item_seleccionado.menu_funcion(item_seleccionado.valor_opcion);

            }
        }

    } while ( (item_seleccionado.tipo_opcion&MENU_OPCION_ESC)==0 && retorno_menu!=MENU_RETORNO_ESC && !salir_todos_menus);


}















int menu_realtape_cond(void)
{
	if (realtape_name==NULL) return 0;
	else return 1;
}


void menu_realtape_pause_unpause(MENU_ITEM_PARAMETERS)
{
	//Este if solo es util cuando se llama desde una tecla F
	if (!menu_realtape_inserted_cond()) return;

	realtape_pause_unpause();
}

//Esto de momento solo se llama desde una tecla F. Lo pongo aqui para que este cerca de las condiciones y acciones de insert
void menu_reinsert_real_tape(void)
{

	debug_printf(VERBOSE_DEBUG,"Running reinsert real tape");

	if (!menu_realtape_cond() ) {
		debug_printf(VERBOSE_DEBUG,"No real inserted to reinsert");
		return;
	}

	//Si esta insertada, expulsar
	if (realtape_inserted.v) {
		debug_printf(VERBOSE_DEBUG,"Ejecting tape");
		realtape_eject();
	}

	//E insertar
	debug_printf(VERBOSE_DEBUG,"Inserting tape");
	realtape_insert();
}




zxvision_window *menu_realtape_record_input_window;

//buffer circular para analisis en esta ventana
//para un frame de video
//#define MENU_REALTAPE_RECORD_INPUT_BUFFER_SIZE (312*2)

//#define MENU_REALTAPE_RECORD_INPUT_BUFFER_SIZE (AUDIO_RECORD_BUFFER_FIFO_SIZE)

//10 frames * 5 = 1 segundo
#define MENU_REALTAPE_RECORD_INPUT_BUFFER_SIZE (AUDIO_RECORD_BUFFER_SIZE*5)

char menu_realtape_record_input_audio_buffer[MENU_REALTAPE_RECORD_INPUT_BUFFER_SIZE];
int menu_realtape_record_input_pos_buffer_write=0;
int menu_realtape_record_input_pos_buffer_initial=0;
int menu_realtape_record_input_onda_onda_congelada=0;

void menu_realtape_record_input_empty_visual_buffer(void)
{
    menu_realtape_record_input_pos_buffer_initial=menu_realtape_record_input_pos_buffer_write=0;
}

void menu_realtape_record_input_write_byte(char valor)
{

    if (menu_realtape_record_input_onda_onda_congelada) return;

    menu_realtape_record_input_audio_buffer[menu_realtape_record_input_pos_buffer_write++]=valor;

    if (menu_realtape_record_input_pos_buffer_write>=MENU_REALTAPE_RECORD_INPUT_BUFFER_SIZE) menu_realtape_record_input_pos_buffer_write=0;

    if (menu_realtape_record_input_pos_buffer_write==menu_realtape_record_input_pos_buffer_initial) {
        menu_realtape_record_input_pos_buffer_initial++;
        if (menu_realtape_record_input_pos_buffer_initial>=MENU_REALTAPE_RECORD_INPUT_BUFFER_SIZE) menu_realtape_record_input_pos_buffer_initial=0;
    }
}

/*int menu_realtape_record_input_get_read(int offset)
{
    int pos=menu_realtape_record_input_pos_buffer_initial+offset;
    if (pos>=MENU_REALTAPE_RECORD_INPUT_BUFFER_SIZE) {
        pos -=MENU_REALTAPE_RECORD_INPUT_BUFFER_SIZE;
    }
    return pos;
}*/


char menu_realtape_record_input_read_byte(void)
{
    if (menu_realtape_record_input_pos_buffer_initial==menu_realtape_record_input_pos_buffer_write) {
        //vacio
        return 0;
    }

    char valor_retorno=menu_realtape_record_input_audio_buffer[menu_realtape_record_input_pos_buffer_initial++];

    if (menu_realtape_record_input_pos_buffer_initial>=MENU_REALTAPE_RECORD_INPUT_BUFFER_SIZE) menu_realtape_record_input_pos_buffer_initial=0;

    return valor_retorno;
}


//Esta función no la estoy usando ya
void menu_realtape_record_input_analize_buffer(zxvision_window *w)
{

    //prueba obtener volumen maximo y minimo
    int max_volumen=0;
    int min_volumen=0;

    int flancos_positivos=0;
    int flancos_negativos=0;
    int valor_anterior=0;

    int longitud=MENU_REALTAPE_RECORD_INPUT_BUFFER_SIZE;

    //int longitud_original=longitud;

    int temp_longitud=20;

    //Bit a 1: 1000 Hz
    //Bit a 0: 2000 Hz
    //Tono guia: 800 Hz


    char *origen=menu_realtape_record_input_audio_buffer;

	for (;longitud>0;longitud--) {

        char valor_escribir=*origen;

        if (valor_escribir>max_volumen) max_volumen=valor_escribir;
        if (valor_escribir<min_volumen) min_volumen=valor_escribir;

        if (valor_escribir>0) {
            if (valor_anterior<=0) flancos_positivos++;
        }

        if (valor_escribir<0) {
            if (valor_anterior>=0) flancos_negativos++;
        }

        valor_anterior=valor_escribir;

        if (temp_longitud>0) {
            printf("%d + %d - %d\n",valor_escribir,flancos_positivos,flancos_negativos);
            temp_longitud--;
        }

        origen++;

	}


       //Calculo frecuencia aproximada

        //int frecuencia=((AUDIO_RECORD_FREQUENCY/longitud_original)*cambiossigno)/2;
        int total_flancos=flancos_positivos+flancos_negativos;

        int frecuencia=((AUDIO_RECORD_FREQUENCY*total_flancos)/MENU_REALTAPE_RECORD_INPUT_BUFFER_SIZE)/2;
        //int frecuencia=((longitud_original*cambiossigno)/AUDIO_RECORD_FREQUENCY)/2;

    printf("Volume max: %d min: %d Freq: %d Hz flancos positivos: %d flancos negativos %d longitud: %d\n",
        max_volumen,min_volumen,frecuencia,flancos_positivos,flancos_negativos,MENU_REALTAPE_RECORD_INPUT_BUFFER_SIZE);

        zxvision_print_string_defaults_format(w,1,0,
            "Volume max: %3d min: %3d Freq: %d Hz flancos positivos: %d flancos negativos %d longitud: %d",
        max_volumen,min_volumen,frecuencia,flancos_positivos,flancos_negativos,MENU_REALTAPE_RECORD_INPUT_BUFFER_SIZE);

        //deducciones
        if (util_abs(frecuencia-800)<100) printf("Deducimos Tono guia\n");
        if (util_abs(frecuencia-1000)<100) printf("Mayoria unos\n");
        if (util_abs(frecuencia-2000)<100) printf("Mayoria ceros\n");


        if (frecuencia>900 && frecuencia<2100) printf("Deducimos 0/1 de spectrum\n");


}

int menu_realtape_record_input_analize_azimuth_frame_animacion=0;
char animacion_string_pilot_tone[]="Pilot tone  ";
char animacion_string_zeros[]=     "0  0  0  0  ";
char animacion_string_unos[]=      "1  1  1  1  ";
char animacion_string_unos_zeros[]="1  0  1  0  ";

void menu_realtape_record_input_analize_azimuth_scroll_string(char *texto)
{
    int longitud=strlen(texto);
    int final=longitud-1;

    int i;
    char inicial=texto[0];
    for (i=0;i<longitud-1;i++) {
        texto[i]=texto[i+1];
    }
    texto[final]=inicial;
}

int menu_realtape_record_input_analize_previo_volumen=0;
int menu_realtape_record_input_analize_volumen_escalado=0;

int menu_realtape_record_input_porcentaje_azimuth=100;
int menu_realtape_record_input_porcentaje_azimuth_antes=0;

int menu_realtape_record_input_aviso_azimuth=0;

//En que linea se indica la info del contenido de la cinta y por tanto aqui se alinea el dibujo del cabezal
#define DRAW_TAPE_MOSTRAR_CONTENIDO_CINTA_LINEA 11

//Analizador de espectro de la señal.Tenemos rangos de frecuencias donde indicamos si hay una señal de ese tipo
#define ANALIZADOR_ESPECTRO_RANGOS 33
//31

//Dado que sampleamos a AUDIO_RECORD_FREQUENCY, el maximo de frecuencia sera eso /2
#define ANALIZADOR_ESPECTRO_MAX_FRECUENCIA (AUDIO_RECORD_FREQUENCY/2)

//Cuenta cuantas ondas en ese rango de frecuencia se encuentran
int analizador_espectro_resultados[ANALIZADOR_ESPECTRO_RANGOS];

//Cuenta el volumen en ese rango de frecuencia se encuentran
int analizador_espectro_resultados[ANALIZADOR_ESPECTRO_RANGOS];

//La suma de todas las amplitudes en cada rango
//Luego se sacara una media
long long int analizador_espectro_resultados_amplitudes[ANALIZADOR_ESPECTRO_RANGOS];



void analizador_espectro_reset(void)
{
    int i;

    for (i=0;i<ANALIZADOR_ESPECTRO_RANGOS;i++) {
        analizador_espectro_resultados[i]=0;
        analizador_espectro_resultados_amplitudes[i]=0;
    }
}

//Retorna que cantidad de rango de frecuencias recoge cada resultado

/*
int analizador_espectro_retorna_tamanyo_resultado(void)
{
    //lo he ajustado con 31 trozos que eso da 503 de tamanyo con frecuencia de 31200 hz. lo apaño para que sea 500
    //return ANALIZADOR_ESPECTRO_MAX_FRECUENCIA/ANALIZADOR_ESPECTRO_RANGOS;
    return 500;
}
*/

int analizador_espectro_retorna_indice(int frecuencia)
{


   /*
    int indice=frecuencia/analizador_espectro_retorna_tamanyo_resultado();
    if (indice>=ANALIZADOR_ESPECTRO_RANGOS) indice=ANALIZADOR_ESPECTRO_RANGOS-1;

    return indice;*/

    /*
    Hasta 2000 Hz: 20 trozos
    Desde 2000 en adelante: 13 trozos
    */

    if (frecuencia<2000) return frecuencia/100;
    else {
        frecuencia-=2000;
        frecuencia /=1000;
        int indice=frecuencia;
        indice +=20;
        if (indice>=ANALIZADOR_ESPECTRO_RANGOS) indice=ANALIZADOR_ESPECTRO_RANGOS-1;
        return indice;
    }
}

//la inversa del anterior, dado un indice dice que rango de frecuencia es
int analizador_espectro_retorna_frec_desde_indice(int indice)
{
    if (indice<20) {
        return indice*100;
    }
    else {
        indice-=20;
        return 2000+indice*1000;
    }
}


void analizador_espectro_registra_frecuencia(int frecuencia,int amplitud)
{
    int indice=analizador_espectro_retorna_indice(frecuencia);
    analizador_espectro_resultados[indice]++;
    analizador_espectro_resultados_amplitudes[indice] +=amplitud;
}

//mostrar resultados en consola
//usando trozos continuos
/*
void old_analizador_espectro_muestra_resultados(zxvision_window *w)
{
    int i;

    int rango_min=0;
    int rango_max;
    int tamanyo_trozo=analizador_espectro_retorna_tamanyo_resultado();

    //resultados tal cual
    for (i=0;i<ANALIZADOR_ESPECTRO_RANGOS;i++) {
        rango_max=rango_min+tamanyo_trozo-1;
        printf("Range %d Hz - %d Hz : %d\n",rango_min,rango_max,analizador_espectro_resultados[i]);
        rango_min +=tamanyo_trozo;
    }

    //Y ahora mediante porcentajes/barras
    //Obtener primero la suma de todos para sacar proporciones
    int suma=0;


    for (i=0;i<ANALIZADOR_ESPECTRO_RANGOS;i++) {
        suma +=analizador_espectro_resultados[i];
    }

    rango_min=0;
    //Y ahora mostrar los trocitos
    for (i=0;i<ANALIZADOR_ESPECTRO_RANGOS;i++) {
        rango_max=rango_min+tamanyo_trozo-1;
        int valor_en_rango=analizador_espectro_resultados[i];

        //sacar porcentaje
        int porcentaje;

        if (suma==0) porcentaje=0;
        else porcentaje=(valor_en_rango*100)/suma;

        //Y mostrarlo
        char buffer_linea[200];
        sprintf(buffer_linea,"Range %5d Hz - %5d Hz : (%3d %%)",rango_min,rango_max,porcentaje);

        int indice_string=strlen(buffer_linea);

        //Maximo 50 de ancho
        int ancho_barra=porcentaje/2;
        int j;
        for (j=0;j<ancho_barra;j++) {
            //printf("=");
            buffer_linea[indice_string++]='=';
        }
        buffer_linea[indice_string]=0;
        printf("%s\n",buffer_linea);

        zxvision_print_string_defaults_fillspc(w,1,i,buffer_linea);


        rango_min +=tamanyo_trozo;
    }

    //Ahora mediante amplitudes de cada trozo
    rango_min=0;
    //Y ahora mostrar los trocitos
    for (i=0;i<ANALIZADOR_ESPECTRO_RANGOS;i++) {
        rango_max=rango_min+tamanyo_trozo-1;
        long long int amplitud_en_rango=analizador_espectro_resultados_amplitudes[i];
        int total_valores=analizador_espectro_resultados[i];

        if (total_valores==0) amplitud_en_rango=0;
        else amplitud_en_rango /=total_valores;

        int volumen=amplitud_en_rango;



        //Y mostrarlo
        char buffer_linea[200];
        sprintf(buffer_linea,"Range %5d Hz - %5d Hz : (%3d vol)",rango_min,rango_max,volumen);

        int indice_string=strlen(buffer_linea);

        //Maximo 256/4 de ancho
        int ancho_barra=volumen/4;
        int j;
        for (j=0;j<ancho_barra;j++) {
            //printf("=");
            buffer_linea[indice_string++]='=';
        }
        buffer_linea[indice_string]=0;
        printf("%s\n",buffer_linea);

        zxvision_print_string_defaults_fillspc(w,1,i,buffer_linea);


        rango_min +=tamanyo_trozo;
    }

}
*/

//0=barras segun amplitud
//1=barras segun porcentaje de cada trozo
//2=segun amplitud y porcentaje
int analizador_spectro_tipo=2;

void analizador_espectro_muestra_resultados(zxvision_window *w,int linea)
{
    int i;




    //Obtener primero la suma de todos para sacar proporciones
    int suma=0;


    for (i=0;i<ANALIZADOR_ESPECTRO_RANGOS;i++) {
        suma +=analizador_espectro_resultados[i];
    }


    //Y ahora mostrar los trocitos
    for (i=0;i<ANALIZADOR_ESPECTRO_RANGOS;i++) {

        //Para porcentajes
        int valor_en_rango=analizador_espectro_resultados[i];

        int porcentaje;

        if (suma==0) porcentaje=0;
        else porcentaje=(valor_en_rango*100)/suma;

        //Para amplitudes
        long long int amplitud_en_rango=analizador_espectro_resultados_amplitudes[i];
        int total_valores=analizador_espectro_resultados[i];

        if (total_valores==0) amplitud_en_rango=0;
        else amplitud_en_rango /=total_valores;

        int volumen=amplitud_en_rango;


        //Y mostrarlo
        char buffer_linea[200];

        //Tipo porcentaje
        if (analizador_spectro_tipo==1) {
            if (porcentaje==0 && valor_en_rango>0) {
                sprintf(buffer_linea,"%5d Hz ( <1 %%)",
                    analizador_espectro_retorna_frec_desde_indice(i));
            }

            else {
                sprintf(buffer_linea,"%5d Hz (%3d %%)",
                    analizador_espectro_retorna_frec_desde_indice(i),porcentaje);
            }

            int indice_string=strlen(buffer_linea);

            //Maximo 50 de ancho
            int ancho_barra=porcentaje/2;
            int j;
            for (j=0;j<ancho_barra;j++) {
                //printf("=");
                buffer_linea[indice_string++]='=';
            }
            buffer_linea[indice_string]=0;
            //printf("%s\n",buffer_linea);

            zxvision_print_string_defaults_fillspc(w,1,linea+i,buffer_linea);
        }

        //Tipo amplitud
        if (analizador_spectro_tipo==0) {
            sprintf(buffer_linea,"%5d Hz ",
                analizador_espectro_retorna_frec_desde_indice(i));

            int indice_string=strlen(buffer_linea);


            int ancho_barra=volumen;



            //No deberia ser mayor, pero por si acaso
            if (ancho_barra>255) ancho_barra=255;



            //Si excede de 50. //Maximo 255/4 de ancho
            if (ancho_barra>50) ancho_barra/=4;
            int j;
            for (j=0;j<ancho_barra;j++) {
                //printf("=");
                buffer_linea[indice_string++]='=';
            }
            buffer_linea[indice_string]=0;
            //printf("%s\n",buffer_linea);

            zxvision_print_string_defaults_fillspc(w,1,linea+i,buffer_linea);
        }

        //Tipo amplitud+porcentaje
        if (analizador_spectro_tipo==2) {
            sprintf(buffer_linea,"%5d Hz ",
                analizador_espectro_retorna_frec_desde_indice(i));

            int indice_string=strlen(buffer_linea);


            int ancho_barra=volumen;



            //No deberia ser mayor, pero por si acaso
            if (ancho_barra>255) ancho_barra=255;

            //Incrementamos porcentaje para tener barras mas grandes
            porcentaje*=2;

            ancho_barra=(ancho_barra*porcentaje)/100;



            //Si excede de 50. //Maximo 255/4 de ancho
            if (ancho_barra>50) ancho_barra/=4;
            int j;
            for (j=0;j<ancho_barra;j++) {
                //printf("=");
                buffer_linea[indice_string++]='=';
            }
            buffer_linea[indice_string]=0;
            //printf("%s\n",buffer_linea);

            zxvision_print_string_defaults_fillspc(w,1,linea+i,buffer_linea);
        }


    }





}

//Estructura para almacenar el analisis de una onda que viene desde fuente externa de sonido (cable, micro, etc)
struct s_input_analize_input_wave {
   //estado:
   //0: desconocido
   //1: leyendo valores<0
   //2: leyendo valores>0

    //Tono guia: length 2168 T-states. = 1238 microsec. Aprox 800 Hz
    /*
    A '0' bit is encoded as 2 pulses of 855 T-states each. = 488 microsec. Aprox 2000 Hz

    A '1' bit is encoded as 2 pulses of 1710 T-states each (ie. twice the length of a '0') = 977 microsec. Aprox 1000 Hz
    */
    int estado_onda;


   //char valor_anterior=0;
   int contador_pulso;
   int contador_pulso_abajo;
   int contador_pulso_arriba;

   int cuantos_ceros;
   int cuantos_unos;
   int cuantos_guias;
   int cuantos_desconocidos;
   int cuantos_total;

   int frecuencia_media_total;

   int valor_max;
   int valor_min;

   int amplitud_media_ceros;
   int amplitud_media_unos;

   int max_absoluto;
   int min_absoluto;
};

struct s_input_analize_input_wave input_analize_input_wave;

//int longitud_temp=100;

void menu_realtape_record_input_analize_azimuth_init(void)
{
    input_analize_input_wave.estado_onda=0;


    //char valor_anterior=0;
    input_analize_input_wave.contador_pulso=0;
    input_analize_input_wave.contador_pulso_abajo=0;
    input_analize_input_wave.contador_pulso_arriba=0;

    input_analize_input_wave.cuantos_ceros=0;
    input_analize_input_wave.cuantos_unos=0;
    input_analize_input_wave.cuantos_guias=0;
    input_analize_input_wave.cuantos_desconocidos=0;
    input_analize_input_wave.cuantos_total=0;

    input_analize_input_wave.frecuencia_media_total=0;

    input_analize_input_wave.valor_max=0;
    input_analize_input_wave.valor_min=0;

    input_analize_input_wave.amplitud_media_ceros=0;
    input_analize_input_wave.amplitud_media_unos=0;

    input_analize_input_wave.max_absoluto=0;
    input_analize_input_wave.min_absoluto=0;


    //temp
    //longitud_temp=100;
}

//Se analiza tipos de datos, si es tono guia, si es azimuth... no solo se analiza el azimuth, pese a que el nombre de la funcion es ese
void menu_realtape_record_input_analize_azimuth(char valor_leido)
{



    if (valor_leido>input_analize_input_wave.max_absoluto) input_analize_input_wave.max_absoluto=valor_leido;
    if (valor_leido<input_analize_input_wave.min_absoluto) input_analize_input_wave.min_absoluto=valor_leido;

    /*if (longitud_temp>0) {
        //printf("Estado %d Leido %d anterior %d\n",estado_onda,valor_leido,valor_anterior);
        longitud_temp--;
    }*/

    switch(input_analize_input_wave.estado_onda) {
        case 0:
            /*if (valor_leido<0) {
                if (longitud_temp>0) printf("cambiamos a pulso debajo\n");
                estado_onda=1;
                contador_pulso=0;
            }*/

            if (valor_leido>0) {
                //if (longitud_temp>0) printf("cambiamos a pulso arriba\n");
                input_analize_input_wave.estado_onda=2;
                input_analize_input_wave.contador_pulso=0;
                input_analize_input_wave.valor_max=input_analize_input_wave.valor_min=0;
            }
        break;

        case 1:
            if (valor_leido>0) {
                //if (longitud_temp>0) printf("Fin pulso abajo\n");
                input_analize_input_wave.estado_onda=2;
                input_analize_input_wave.contador_pulso_abajo=input_analize_input_wave.contador_pulso;

                input_analize_input_wave.contador_pulso=0;

                //Tiempo de arriba, abajo indica fin de bit
                //if (longitud_temp>0) {
                    /*if (longitud_temp>0) printf("Fin onda. arriba %d abajo %d\n",
                        input_analize_input_wave.contador_pulso_arriba,input_analize_input_wave.contador_pulso_abajo);*/

                    //Calcular microsegundos
                    //AUDIO_RECORD_FREQUENCY
                    //int longitud_una_lectura=1000000/AUDIO_RECORD_FREQUENCY;

                    //int microsec_onda=(contador_pulso_abajo+contador_pulso_arriba)*longitud_una_lectura;


                    int microsec_onda=(input_analize_input_wave.contador_pulso_abajo+input_analize_input_wave.contador_pulso_arriba)*1000000/AUDIO_RECORD_FREQUENCY;

                    /*if (longitud_temp>0) printf("Onda tarda (%d %d) %d microsec (0=488, 1=977, guia=1238)\n",
                        input_analize_input_wave.contador_pulso_abajo,input_analize_input_wave.contador_pulso_arriba,microsec_onda);*/

                    int frecuencia;

                    if (microsec_onda==0) frecuencia=0;
                    else frecuencia=1000000/microsec_onda;

                    //if (longitud_temp>0) printf("Frecuencia de ese trozo de onda: %d Hz\n",frecuencia);


                    int amplitud=input_analize_input_wave.valor_max-input_analize_input_wave.valor_min;

                    analizador_espectro_registra_frecuencia(frecuencia,amplitud);

                    //la tolerancia entre senyales
                    //int rango_arriba_abajo=130;

                    /*
                    Tono guia entre 358 y <618 (centro en 488)
                    Unos entre 847 y <1107 (centro en 977). aunque le damos mayor tolerancia
                    Ceros entre 1108 y <1368 (centro en 1238)

                    Nota: aun en una carga normal de spectrum pueden haber desconocidos pues estamos analizando un trozo
                    de buffer que puede empezar y acabar en media onda, o sea, la logica dice que si la carga es spectrum,
                    maximo podemos encontrar dos ondas desconocidas, el resto tienen que ser de spectrum
                    Nota2: no estoy considerando la onda de sincronismo que viene justo despues del tono guia y antes de los datos,
                    por tanto eso también se puede considerar como desconocido
                    */

                    if (microsec_onda>=358 && microsec_onda<618) {
                        //int amplitud=valor_max-valor_min;
                        //printf("es un 0. amplitud %d microsec: %d\n",amplitud,microsec_onda);
                        input_analize_input_wave.amplitud_media_ceros+=amplitud;
                        input_analize_input_wave.cuantos_ceros++;
                    }
                    else if (microsec_onda>=618 && microsec_onda<1107) {
                        //int amplitud=valor_max-valor_min;
                        //printf("es un 1. amplitud %d microsec: %d\n",amplitud,microsec_onda);
                        input_analize_input_wave.amplitud_media_unos+=amplitud;
                        input_analize_input_wave.cuantos_unos++;
                    }
                    else if (microsec_onda>=1107 && microsec_onda<1368) {
                        //printf("es tono guia\n");
                        input_analize_input_wave.cuantos_guias++;
                    }
                    else {
                        //printf("Es desconocido. microsec_onda: %d amplitud: %d\n",microsec_onda,amplitud);
                        input_analize_input_wave.cuantos_desconocidos++;
                    }

                    //Incrementar total pulsos (ceros, unos, etc)
                    input_analize_input_wave.frecuencia_media_total +=frecuencia;
                    input_analize_input_wave.cuantos_total++;

                    input_analize_input_wave.valor_max=input_analize_input_wave.valor_min=0;
                //}
            }
            else {
                if (valor_leido<input_analize_input_wave.valor_min) input_analize_input_wave.valor_min=valor_leido;
            }
        break;

        case 2:
            if (valor_leido<0) {
                //if (longitud_temp>0) printf("Fin pulso arriba\n");
                input_analize_input_wave.estado_onda=1;

                input_analize_input_wave.contador_pulso_arriba=input_analize_input_wave.contador_pulso;

                input_analize_input_wave.contador_pulso=0;
            }
            else {
                if (valor_leido>input_analize_input_wave.valor_max) input_analize_input_wave.valor_max=valor_leido;
            }
        break;



    }

    input_analize_input_wave.contador_pulso++;


}

int menu_realtape_record_input_analize_azimuth_end_conta_segundo=0;

int menu_realtape_record_input_analize_azimuth_tiempo_deteccion_spectrum=0;

int menu_realtape_record_input_estimacion_bits=0;

void menu_realtape_record_input_print_unknown_average_freq(zxvision_window *w,int linea)
{
    if (audio_is_recording_input) {
        zxvision_print_string_defaults_fillspc_format(w,1,linea,"Unknown signal. Average frequency: %5d Hz",input_analize_input_wave.frecuencia_media_total);
    }
}

void menu_realtape_record_input_analize_azimuth_end(zxvision_window *w,int linea)
{

    if (input_analize_input_wave.cuantos_ceros==0) input_analize_input_wave.amplitud_media_ceros=0;
    else input_analize_input_wave.amplitud_media_ceros /=input_analize_input_wave.cuantos_ceros;

    if (input_analize_input_wave.cuantos_unos==0) input_analize_input_wave.amplitud_media_unos=0;
    else input_analize_input_wave.amplitud_media_unos /=input_analize_input_wave.cuantos_unos;

    if (input_analize_input_wave.cuantos_total==0) input_analize_input_wave.frecuencia_media_total=0;
    else input_analize_input_wave.frecuencia_media_total /=input_analize_input_wave.cuantos_total;

    /*
    printf("Total ceros: %d Total unos: %d Tonos guias: %d Desconocidos: %d amplitud ceros %d amplitud unos %d\n",
        input_analize_input_wave.cuantos_ceros,input_analize_input_wave.cuantos_unos,
        input_analize_input_wave.cuantos_guias,input_analize_input_wave.cuantos_desconocidos,
        input_analize_input_wave.amplitud_media_ceros,input_analize_input_wave.amplitud_media_unos);
    */

    //solo mostrar info cuando señal sea de Spectrum

    //borrar primero esas lineas
    //Volumen Max min
    //Recording to file
    //Guessed
    //Signal type
    //Azimuth
    zxvision_print_string_defaults_fillspc(w,1,linea,"");
    zxvision_print_string_defaults_fillspc(w,1,linea+1,"");
    zxvision_print_string_defaults_fillspc(w,1,linea+2,"");
    zxvision_print_string_defaults_fillspc(w,1,linea+3,"");
    //zxvision_print_string_defaults_fillspc(w,1,linea+4,"");


    int volumen_absoluto=input_analize_input_wave.max_absoluto;
    if (util_get_absolute(input_analize_input_wave.min_absoluto)>input_analize_input_wave.max_absoluto) volumen_absoluto=util_get_absolute(input_analize_input_wave.min_absoluto);

    menu_realtape_record_input_analize_volumen_escalado=volumen_absoluto;

    //Ahora tenemos valor entre 0 y 128. Pasar a entre 0 y 15
    //esto se hace para el vu-meter
    //int valor_escalado=(mayor*16)/128;
    menu_realtape_record_input_analize_volumen_escalado=(menu_realtape_record_input_analize_volumen_escalado*16)/128;

    //Vigilar que no pase de 15
    if (menu_realtape_record_input_analize_volumen_escalado>15) menu_realtape_record_input_analize_volumen_escalado=15;



	menu_realtape_record_input_analize_previo_volumen=menu_decae_ajusta_valor_volumen(menu_realtape_record_input_analize_previo_volumen,
        menu_realtape_record_input_analize_volumen_escalado);

	char texto_volumen[32];
    menu_string_volumen(texto_volumen,menu_realtape_record_input_analize_volumen_escalado,menu_realtape_record_input_analize_previo_volumen);
                                                                //"Volume C: %s"


	//zxvision_print_string_defaults_fillspc(menu_audio_draw_sound_wave_window,1,2,buffer_texto_medio);


    //zxvision_print_string_defaults_fillspc_format(w,1,linea++,"Volume: Max %3d Min %4d Current %3d %s",
    //    input_analize_input_wave.max_absoluto,input_analize_input_wave.min_absoluto,volumen_absoluto,texto_volumen);

    zxvision_print_string_defaults_fillspc_format(w,1,linea++,"Volume: Max %3d Min %4d %s",
        input_analize_input_wave.max_absoluto,input_analize_input_wave.min_absoluto,texto_volumen);

    if (audiorecord_input_write_to_disk_enabled) {
        int minutos,segundos;
        audiorecord_input_write_to_disk_time_size(&minutos,&segundos);

        zxvision_print_string_defaults_fillspc_format(w,1,linea,"^^Recording to file^^ %02d:%02d",minutos,segundos);

    }

    linea++;



    //Minimo valor a partir del cual se considera que es un tipo de onda u otra
    int minimo_ondas=3;

    //La mayoria de veces habrá una onda desconocida al principio, dado que empezamos a leer el buffer
    //en una posicion indeterminada. Por tanto, descarto ese primer desconocido
    //if (input_analize_input_wave.cuantos_desconocidos>0) input_analize_input_wave.cuantos_desconocidos--;

    if (
        (
            input_analize_input_wave.cuantos_desconocidos<input_analize_input_wave.cuantos_unos ||
            input_analize_input_wave.cuantos_desconocidos<input_analize_input_wave.cuantos_ceros ||
            input_analize_input_wave.cuantos_desconocidos<input_analize_input_wave.cuantos_guias
        )

        &&

        (
            input_analize_input_wave.cuantos_unos>minimo_ondas ||
            input_analize_input_wave.cuantos_ceros>minimo_ondas ||
            input_analize_input_wave.cuantos_guias>minimo_ondas
        )

        ) {

        //Si es el primer fragmento que se ha detectado spectrum
        if (menu_realtape_record_input_analize_azimuth_tiempo_deteccion_spectrum==0) {
            menu_realtape_record_input_analize_azimuth_tiempo_deteccion_spectrum=contador_segundo_infinito;
        }

        //Si ha pasado medio segundo detectando spectrum, indicarlo al usuario
        //Si mostrasemos al instante el tipo de carga, sin esperar este medio segundo,
        //puede detectar fragmentos cortos (el del sample examinado aqui, 20 ms) donde haya sonidos con frecuencias
        //en el rango de spectrum , y luego otros fragmentos que no, por tanto el texto de que es Spectrum va apareciendo
        //y desapareciendo y para el usuario es confuso. Mejor que al menos haya medio segundo seguido de todas las frecuencias
        //en el rango esperado
        if (contador_segundo_infinito-menu_realtape_record_input_analize_azimuth_tiempo_deteccion_spectrum>500) {

            zxvision_print_string_defaults_fillspc(w,1,linea++,"Guessed ZX Spectrum loading tone");

            char buffer_signal_type[50];
            buffer_signal_type[0]=0;

            //Estimacion del total de bits leidos - Se resetea al detectar algo que no son ceros ni unos

            //Analisis del tipo de onda
            //Si mayoria tono guia
            if (input_analize_input_wave.cuantos_guias>input_analize_input_wave.cuantos_unos &&
                input_analize_input_wave.cuantos_guias>input_analize_input_wave.cuantos_ceros) {
                zxvision_print_string_defaults_fillspc_format(w,1,linea,"Signal type: Pilot tone (%d Hz)",input_analize_input_wave.frecuencia_media_total);
                strcpy(buffer_signal_type,animacion_string_pilot_tone);

                //Si es tono guia, borrar mensaje de la linea siguiente donde informa de ajuste de azimuth,
                //porque se puede haber quedado de antes y en caso de tono guia no deducimos azimuth
                zxvision_print_string_defaults_fillspc_format(w,1,linea+1,"");

                //Y decimos cabezal perfectamente alineado
                menu_realtape_record_input_porcentaje_azimuth_antes=menu_realtape_record_input_porcentaje_azimuth;
                menu_realtape_record_input_porcentaje_azimuth=100;

                menu_realtape_record_input_aviso_azimuth=0;

                //Si ha cambiado valor azimuth, avisar para refrescar toda pantalla
                if (menu_realtape_record_input_porcentaje_azimuth_antes!=menu_realtape_record_input_porcentaje_azimuth) {
                    w->must_clear_cache_on_draw_once=1;
                }

                menu_realtape_record_input_estimacion_bits=0;
            }
            else if (input_analize_input_wave.cuantos_guias<minimo_ondas &&
            input_analize_input_wave.cuantos_unos<minimo_ondas) {
                //Los bytes los indico con "~" porque es una cantidad aproximada, porque analizamos
                //el buffer para saber el tipo de datos (tono guia, bits, etc) a nivel estadístico,
                //no parseamos perfectamente los 0 y 1 (pues pueden quedar a medias a final de un buffer por ejemplo),
                //por tanto la cantidad mostrada suele ser menor a los bytes realmente leidos,
                //mientras mas leemos, mas "pierde" la cuenta, porque hay mas bits que se cortan a final de buffer
                zxvision_print_string_defaults_fillspc_format(w,1,linea,"Signal type: Most zeroes (%5d Hz) (~%d B)",
                    input_analize_input_wave.frecuencia_media_total,menu_realtape_record_input_estimacion_bits/8);
                strcpy(buffer_signal_type,animacion_string_zeros);

                menu_realtape_record_input_estimacion_bits +=input_analize_input_wave.cuantos_unos;
                menu_realtape_record_input_estimacion_bits +=input_analize_input_wave.cuantos_ceros;
            }

            else if (input_analize_input_wave.cuantos_guias<minimo_ondas &&
                input_analize_input_wave.cuantos_ceros<minimo_ondas) {
                zxvision_print_string_defaults_fillspc_format(w,1,linea,"Signal type: Most ones   (%5d Hz) (~%d B)",
                    input_analize_input_wave.frecuencia_media_total,menu_realtape_record_input_estimacion_bits/8);
                strcpy(buffer_signal_type,animacion_string_unos);

                menu_realtape_record_input_estimacion_bits +=input_analize_input_wave.cuantos_unos;
                menu_realtape_record_input_estimacion_bits +=input_analize_input_wave.cuantos_ceros;

            }

            else {
                zxvision_print_string_defaults_fillspc_format(w,1,linea,"Signal type: Mixed zeros and ones   (~%d B)",
                    menu_realtape_record_input_estimacion_bits/8);
                strcpy(buffer_signal_type,animacion_string_unos_zeros);

                menu_realtape_record_input_estimacion_bits +=input_analize_input_wave.cuantos_unos;
                menu_realtape_record_input_estimacion_bits +=input_analize_input_wave.cuantos_ceros;

            }

            //zxvision_print_string_defaults_fillspc_format(w,1,linea,"Signal type: %s",buffer_signal_type);

            zxvision_print_string_defaults_fillspc(w,1,DRAW_TAPE_MOSTRAR_CONTENIDO_CINTA_LINEA,buffer_signal_type);

            linea++;

            //Analisis de Azimuth
            //Solo analizar amplitud cuando hay cantidad suficiente de ceros y unos y amplitud mayor que un minimo
            /*
            Por lo que lei, el ajuste de azimuth correcto, el que esta justo perpendicular a la lectura de la cinta,
            proporciona mejor nivel en treble (agudos?) cosa que tiene sentido en Spectrum:
            Al calibrar bien el azimuth en una carga de Spectrum, se oye mas agudo, y esto tiene mas lógica aun porque
            los bits de 0 tienen mayor amplitud (que son los bits mas cortos, con mas frecuencia y por tanto mas agudos)
            Si el azimuth esta mal, los bits de 0 tiene menor amplitud, y por tanto se oye mas grave

            //Solo analizo azimuth cuando amplitudes > 2, con menos amplitud no tiene sentido
            */
            if (input_analize_input_wave.cuantos_unos>minimo_ondas &&
                input_analize_input_wave.cuantos_ceros>minimo_ondas &&
                input_analize_input_wave.amplitud_media_unos>2 &&
                input_analize_input_wave.amplitud_media_ceros>2) {
                int porcentaje_amplitud;

                if (input_analize_input_wave.amplitud_media_unos==0) porcentaje_amplitud=0;
                else porcentaje_amplitud=(input_analize_input_wave.amplitud_media_ceros*100)/input_analize_input_wave.amplitud_media_unos;

                //Los unos tienen que ocupar mas siempre. Por tanto porcentaje de  > 100% no deberia tener sentido
                if (porcentaje_amplitud>100) porcentaje_amplitud=100;


                //Si es menos del 75%

                zxvision_print_string_defaults_fillspc_format(w,1,linea,"Azimuth Accuracy %3d %% %s",porcentaje_amplitud,
                    (porcentaje_amplitud<75 ? "Adjust AZIMUTH!!" : "OK"));

                if (porcentaje_amplitud<75) menu_realtape_record_input_aviso_azimuth=1;
                else menu_realtape_record_input_aviso_azimuth=0;

                menu_realtape_record_input_porcentaje_azimuth_antes=menu_realtape_record_input_porcentaje_azimuth;

                menu_realtape_record_input_porcentaje_azimuth=porcentaje_amplitud;

                //Si ha cambiado valor azimuth, avisar para refrescar toda pantalla
                if (menu_realtape_record_input_porcentaje_azimuth_antes!=menu_realtape_record_input_porcentaje_azimuth) {
                    w->must_clear_cache_on_draw_once=1;
                }

            }
        }

        else {
            //Borrar linea deteccion azimuth si ha pasado 2 segundos al menos
            if (menu_realtape_record_input_analize_azimuth_end_conta_segundo-contador_segundo>50*2) {
                zxvision_print_string_defaults_fillspc_format(w,1,linea,"");

                menu_realtape_record_input_print_unknown_average_freq(w,linea);
            }

            menu_realtape_record_input_estimacion_bits=0;
        }




    }

    else {
        //Resetear contador de tiempo de deteccion
        //printf("No detectada senyal spectrum. %d\n",contador_segundo_infinito);
        menu_realtape_record_input_analize_azimuth_tiempo_deteccion_spectrum=0;

        menu_realtape_record_input_print_unknown_average_freq(w,linea);
    }

    menu_realtape_record_input_analize_azimuth_end_conta_segundo=contador_segundo;





    menu_realtape_record_input_analize_azimuth_frame_animacion++;
    if (menu_realtape_record_input_analize_azimuth_frame_animacion % 10==0) {

        menu_realtape_record_input_analize_azimuth_scroll_string(animacion_string_pilot_tone);
        menu_realtape_record_input_analize_azimuth_scroll_string(animacion_string_zeros);
        menu_realtape_record_input_analize_azimuth_scroll_string(animacion_string_unos);
        menu_realtape_record_input_analize_azimuth_scroll_string(animacion_string_unos_zeros);
    }



}

void menu_realtape_record_input_draw_waveform_putpixel(zxvision_window *w,int x,int y,int color)
{
    zxvision_putpixel(w,x,y,color);
}

//para limpiar solo sobre los maximos que haya habido
int menu_realtape_record_input_inicial_limpieza=1;
int menu_realtape_record_input_old_max=0;
int menu_realtape_record_input_old_min=1000;

//int temp_color;

int menu_realtape_record_input_antes_y_arriba=0;
int menu_realtape_record_input_antes_y_abajo=0;

//0=linea continua, 1=relleno, 2=solo puntos
int menu_realtape_record_input_tipo_onda=0;



void menu_realtape_record_input_draw_waveform(zxvision_window *w,int x_orig,int y_orig,int ancho,int alto)
{

    //Mostrar limites
    /*
    zxvision_putpixel(w,x_orig,y_orig,ESTILO_GUI_COLOR_AVISO);
    zxvision_putpixel(w,x_orig+ancho-1,y_orig,ESTILO_GUI_COLOR_AVISO);
    zxvision_putpixel(w,x_orig,y_orig+alto-1,ESTILO_GUI_COLOR_AVISO);
    zxvision_putpixel(w,x_orig+ancho-1,y_orig+alto-1,ESTILO_GUI_COLOR_AVISO);

    zxvision_draw_line(w,x_orig,y_orig,x_orig+ancho-1,y_orig,
        ESTILO_GUI_COLOR_AVISO,menu_realtape_record_input_draw_waveform_putpixel);

    zxvision_draw_line(w,x_orig,y_orig+alto-1,x_orig+ancho-1,y_orig+alto-1,
        ESTILO_GUI_COLOR_AVISO,menu_realtape_record_input_draw_waveform_putpixel);
    */

    //primero limpiar zona
    if (menu_realtape_record_input_inicial_limpieza) zxvision_draw_filled_rectangle(w,x_orig,y_orig,ancho,alto,ESTILO_GUI_PAPEL_NORMAL);
    else {
        int alto_borrar=menu_realtape_record_input_old_max-menu_realtape_record_input_old_min+1;
        zxvision_draw_filled_rectangle(w,x_orig,menu_realtape_record_input_old_min,ancho,alto_borrar,ESTILO_GUI_PAPEL_NORMAL);
        //if (temp_color==16) temp_color=0;

        menu_realtape_record_input_old_max=0;
        menu_realtape_record_input_old_min=1000;
    }

    menu_realtape_record_input_inicial_limpieza=0;

    //printf("ancho %d alto %d\n",ancho,alto);

    //Solo leer el trozo que significa un frame de video
    int longitud_dibujar=312*2;

    int i;
    int trozos_horiz=longitud_dibujar/ancho;

    if (trozos_horiz<1) trozos_horiz=1;

    int pos_en_trozo=0;
    int valor_medio=0;

    int alto_medio=alto/2;
    int valor_max=0;
    int valor_min=0;

    int x=0;

    y_orig=y_orig+alto/2;

    menu_realtape_record_input_antes_y_arriba=menu_realtape_record_input_antes_y_abajo=y_orig;

    //si onda congelada, posicion lectura inicial siempre la misma

    for (i=0;i<longitud_dibujar;i++) {
        //int pos=menu_realtape_record_input_get_read(i);
        //char valor_leido=menu_realtape_record_input_audio_buffer[pos];

        char valor_leido=menu_realtape_record_input_read_byte();

        menu_realtape_record_input_analize_azimuth(valor_leido);

        if (menu_realtape_record_input_onda_onda_congelada) valor_leido=menu_realtape_record_input_audio_buffer[i];

        //Si invertir señal en entrada, mostrarla tambien invertida
        if (realtape_last_value_invert_signal.v) valor_leido=-valor_leido;

        if (valor_leido>valor_max) valor_max=valor_leido;
        if (valor_leido<valor_min) valor_min=valor_leido;

        //temp
        //valor_max=127;
        //valor_min=-128;

        valor_medio +=valor_leido;

        //printf("%d\n",valor_medio);

        pos_en_trozo++;
        if (pos_en_trozo==trozos_horiz) {



            //printf("valor_medio antes %d trozos %d\n",valor_medio,trozos_horiz);
            valor_medio /=trozos_horiz;

            valor_medio=(valor_medio*alto_medio)/128;
            //printf("valor_medio despues %d\n",valor_medio);
            int pos_y=y_orig-valor_medio; //positivo hacia arriba

            //zxvision_putpixel(w,x_orig+x,pos_y,ESTILO_GUI_COLOR_WAVEFORM);


            //Valor de arriba
            //valor_max /=trozos_horiz;
            valor_max=(valor_max*alto_medio)/128;
            pos_y=y_orig-valor_max; //positivo hacia arriba

            //zxvision_putpixel(w,x_orig+x,pos_y,ESTILO_GUI_COLOR_WAVEFORM);
            if (pos_y<menu_realtape_record_input_old_min) menu_realtape_record_input_old_min=pos_y;

            //0=linea continua, 1=relleno, 2=solo puntos
            //int menu_realtape_record_input_tipo_onda=0;

            //solo punto
            if (menu_realtape_record_input_tipo_onda==2) zxvision_putpixel(w,x_orig+x,pos_y,ESTILO_GUI_COLOR_WAVEFORM);

            //linea continuada
            if (menu_realtape_record_input_tipo_onda==0) {
                if (x>0) {
                    zxvision_draw_line(w,x_orig+x-1,menu_realtape_record_input_antes_y_arriba,x_orig+x,pos_y,
                    ESTILO_GUI_COLOR_WAVEFORM,menu_realtape_record_input_draw_waveform_putpixel);

                    menu_realtape_record_input_antes_y_arriba=pos_y;
                }
            }

            //linea rellena
            if (menu_realtape_record_input_tipo_onda==1) {
                zxvision_draw_line(w,x_orig+x,y_orig,x_orig+x,pos_y,ESTILO_GUI_COLOR_WAVEFORM,menu_realtape_record_input_draw_waveform_putpixel);
            }

            //Valor de abajo
            //valor_min /=trozos_horiz;
            valor_min=(valor_min*alto_medio)/128;
            pos_y=y_orig-valor_min; //negativo hacia abajo
            //zxvision_putpixel(w,x_orig+x,pos_y,ESTILO_GUI_COLOR_WAVEFORM);
            if (pos_y>menu_realtape_record_input_old_max) menu_realtape_record_input_old_max=pos_y;

            //solo punto
            if (menu_realtape_record_input_tipo_onda==2) zxvision_putpixel(w,x_orig+x,pos_y,ESTILO_GUI_COLOR_WAVEFORM);

            //linea continuada
            if (menu_realtape_record_input_tipo_onda==0) {
                if (x>0) {
                    zxvision_draw_line(w,x_orig+x-1,menu_realtape_record_input_antes_y_abajo,x_orig+x,pos_y,
                    ESTILO_GUI_COLOR_WAVEFORM,menu_realtape_record_input_draw_waveform_putpixel);

                    menu_realtape_record_input_antes_y_abajo=pos_y;
                }
            }

            //linea rellena
            if (menu_realtape_record_input_tipo_onda==1) {
                zxvision_draw_line(w,x_orig+x,y_orig,x_orig+x,pos_y,ESTILO_GUI_COLOR_WAVEFORM,menu_realtape_record_input_draw_waveform_putpixel);
            }



            pos_en_trozo=0;
            valor_medio=0;

            valor_max=valor_min=0;


            x++;
        }
    }

        //Indicar y=0
        //zxvision_putpixel(w,x_orig+x,y_orig,ESTILO_GUI_TINTA_NORMAL);
        zxvision_draw_line(w,x_orig,y_orig,x_orig+ancho-1,y_orig,ESTILO_GUI_TINTA_NORMAL,menu_realtape_record_input_draw_waveform_putpixel);

}



int menu_realtape_record_input_show_previo_value=0;

int menu_realtape_record_input_show_info(zxvision_window *w,int linea)
{
        //Record audio buffer
        int tamanyo_buffer_audio,posicion_buffer_audio;
		//audio_get_buffer_info(&tamanyo_buffer_audio,&posicion_buffer_audio);
        tamanyo_buffer_audio=audiorecord_input_return_fifo_total_size();
        posicion_buffer_audio=audiorecord_input_fifo_return_size();

        int perc_audio;
        //mostrar una barra de llenado del buffer
        //usa las mismas funciones de volumen de AY chip donde el maximo es 15
        //int barra_volumen;

        if (tamanyo_buffer_audio==0) {
            perc_audio=0;
            //barra_volumen=15;
        }

        else {
            perc_audio=(posicion_buffer_audio*100)/tamanyo_buffer_audio;
            //barra_volumen=(posicion_buffer_audio*15)/tamanyo_buffer_audio;
        }

        char buf_volumen_canal[32];
        char texto_buffer[100];
        //menu_string_volumen(buf_volumen_canal,barra_volumen,core_statistics_previo_audio_buffer);

        //core_statistics_previo_audio_buffer=barra_volumen;

        menu_realtape_record_input_show_previo_value=menu_string_volumen_maxmin(buf_volumen_canal,posicion_buffer_audio,
                                                    menu_realtape_record_input_show_previo_value,tamanyo_buffer_audio);
        if (!audio_is_recording_input) {
            strcpy (texto_buffer,"Record Buffer: (Not Recording). ~~e: enable");
        }

        else {
            if (audiorecord_last_write_full) {
                sprintf (texto_buffer,"Record Buffer: FULL!");

                //Resetear el aviso de lleno si ha pasado 2 segundos
                if (contador_segundo_infinito-audiorecord_last_write_full>1000*2) {
                    //printf("Resetear . contador segundo %d last %d\n",contador_segundo_infinito,audiorecord_last_write_full);
                    audiorecord_last_write_full=0;
                }


            }

            else {

                sprintf (texto_buffer,"Record Buffer: %6d/%6d (%3d%%) [%s]",
                    posicion_buffer_audio,tamanyo_buffer_audio,perc_audio,buf_volumen_canal);
            }
        }

        //core_statistics_last_perc_audio=perc_audio;

        zxvision_print_string_defaults_fillspc(w,1,linea++,texto_buffer);

        return linea;
}

int menu_realtape_record_input_overlay_segundo_anterior=0;



#define DRAW_TAPE_ANCHO_CABEZAL 40
#define DRAW_TAPE_ALTO_CABEZAL 30
#define DRAW_TAPE_ANCHO_TORNILLO (DRAW_TAPE_ANCHO_CABEZAL/10)
#define DRAW_TAPE_ALTO_TORNILLO (DRAW_TAPE_ALTO_CABEZAL/3)
#define DRAW_TAPE_ANCHO_CABEZA_TORNILLO (DRAW_TAPE_ANCHO_TORNILLO*2)
#define DRAW_TAPE_SEPARACION_CABEZAL_CINTA_Y (DRAW_TAPE_ALTO_CABEZAL/5)


//yactual es la parte de arriba del tornillo
void menu_realtape_record_input_draw_tape_tornillo(zxvision_window *w,int xactual,int yactual,int alto_tornillo,int color)
{



    //ancho del tornillo
    zxvision_draw_filled_rectangle(w,xactual,yactual,DRAW_TAPE_ANCHO_TORNILLO,alto_tornillo,color);

    //cabeza del tornillo
    int desplazamiento_cabeza=(DRAW_TAPE_ANCHO_CABEZA_TORNILLO-DRAW_TAPE_ANCHO_TORNILLO)/2;

    xactual -=desplazamiento_cabeza;
    //printf("%d %d---\n",DRAW_TAPE_ANCHO_CABEZA_TORNILLO,DRAW_TAPE_ANCHO_TORNILLO);
    zxvision_draw_line(w,xactual,yactual,xactual+DRAW_TAPE_ANCHO_CABEZA_TORNILLO-1,yactual,color,menu_realtape_record_input_draw_waveform_putpixel);
    zxvision_draw_line(w,xactual+1,yactual-1,xactual+DRAW_TAPE_ANCHO_CABEZA_TORNILLO-2,yactual-1,color,menu_realtape_record_input_draw_waveform_putpixel);

}


//para la animacion de los puntitos que se mueven en la derecha de la cinta
int menu_realtape_record_input_frame_puntitos=0;
#define MAX_FRAMES_PUNTITOS 90

void menu_realtape_record_input_draw_tape_puntitos(zxvision_window *w,int frame,int color,int x,int y)
{

    x +=MAX_FRAMES_PUNTITOS-20;



    zxvision_putpixel(w,x-(frame % MAX_FRAMES_PUNTITOS),y,color);

    zxvision_putpixel(w,x-((frame+5) % MAX_FRAMES_PUNTITOS),y+5,color);

    zxvision_putpixel(w,x-((frame+15) % MAX_FRAMES_PUNTITOS),y-3,color);

    zxvision_putpixel(w,x-((frame+24) % MAX_FRAMES_PUNTITOS),y-7,color);

    zxvision_putpixel(w,x-((frame+30) % MAX_FRAMES_PUNTITOS),y-7,color);

    zxvision_putpixel(w,x-((frame+60) % MAX_FRAMES_PUNTITOS),y+2,color);


}

void menu_realtape_record_input_draw_tape_aux(zxvision_window *w,int angulo_rotacion,int origen_x,int origen_y)
{

    //coordenada x donde esta la derecha de la base del cabezal
    //int origen_x=200;
    //coordenada y donde esta la derecha de la base del cabezal
    //int origen_y=100;

    int ancho_base=80;

    int ancho_cabezal=DRAW_TAPE_ANCHO_CABEZAL;
    int alto_cabezal=DRAW_TAPE_ALTO_CABEZAL;

    int xactual=origen_x;
    int yactual=origen_y;

    int xfinal,yfinal;

    int yfinalbase;

    //base
    util_move_turtle(xactual,yactual,180-angulo_rotacion,ancho_base,&xfinal,&yfinal);
    zxvision_draw_line(w,xactual,yactual,xfinal,yfinal,ESTILO_GUI_TINTA_NORMAL,menu_realtape_record_input_draw_waveform_putpixel);
    //xactual=xfinal;
    //yactual=yfinal;
    //Guardar posicion Y para luego dibujar tornillo izquierda
    yfinalbase=yfinal;

    //movernos hasta donde empieza el cabezal
    util_move_turtle(origen_x,origen_y,180-angulo_rotacion,(ancho_base-ancho_cabezal)/2,&xfinal,&yfinal);
    xactual=xfinal;
    yactual=yfinal;

    //cabezal derecha vertical
    util_move_turtle(xactual,yactual,90-angulo_rotacion,alto_cabezal,&xfinal,&yfinal);
    zxvision_draw_line(w,xactual,yactual,xfinal,yfinal,ESTILO_GUI_TINTA_NORMAL,menu_realtape_record_input_draw_waveform_putpixel);
    xactual=xfinal;
    yactual=yfinal;

    //cabezal arriba horizontal
    util_move_turtle(xactual,yactual,180-angulo_rotacion,ancho_cabezal,&xfinal,&yfinal);
    zxvision_draw_line(w,xactual,yactual,xfinal,yfinal,ESTILO_GUI_TINTA_NORMAL,menu_realtape_record_input_draw_waveform_putpixel);
    xactual=xfinal;
    yactual=yfinal;

    //cabezal derecha vertical
    util_move_turtle(xactual,yactual,270-angulo_rotacion,alto_cabezal,&xfinal,&yfinal);
    zxvision_draw_line(w,xactual,yactual,xfinal,yfinal,ESTILO_GUI_TINTA_NORMAL,menu_realtape_record_input_draw_waveform_putpixel);
    xactual=xfinal;
    yactual=yfinal;


    //Dibujar trozo de linea de lectura dentro de cabezal
    //ancho sera la mitad de ancho del cabezal
    //y alto seran 3 lineas
    int ancho_lectura_cabezal=ancho_cabezal/2;

    //movernos hasta donde empieza el cabezal, en X
    util_move_turtle(origen_x,origen_y,180-angulo_rotacion,(ancho_base-ancho_cabezal)/2,&xfinal,&yfinal);
    xactual=xfinal;
    yactual=yfinal;
    //y un poco mas hasta donde empieza el trozo de lectura, por la derecha
    util_move_turtle(xactual,yactual,180-angulo_rotacion,(ancho_cabezal-ancho_lectura_cabezal)/2,&xfinal,&yfinal);
    xactual=xfinal;
    yactual=yfinal;

    //Y subir el trozo
    int subir_trozo=(alto_cabezal/4)*3;
    util_move_turtle(xactual,yactual,90-angulo_rotacion,subir_trozo,&xfinal,&yfinal);
    xactual=xfinal;
    yactual=yfinal;

    //Dibujar 3 lineas, hacia la izquierda
    util_move_turtle(xactual,yactual,180-angulo_rotacion,ancho_lectura_cabezal,&xfinal,&yfinal);
    zxvision_draw_line(w,xactual,yactual,xfinal,yfinal,ESTILO_GUI_TINTA_NORMAL,menu_realtape_record_input_draw_waveform_putpixel);
    xactual=xfinal;
    yactual=yfinal;

    //bajo 1
    //util_move_turtle(xactual,yactual,270-angulo_rotacion,1,&xfinal,&yfinal);
    //zxvision_draw_line(w,xactual,yactual,xfinal,yfinal,ESTILO_GUI_TINTA_NORMAL,menu_realtape_record_input_draw_waveform_putpixel);
    yactual++;

    //A la derecha, aprovechando donde estabamos, como si fuera una impresora matricial
    util_move_turtle(xactual,yactual,-angulo_rotacion,ancho_lectura_cabezal,&xfinal,&yfinal);
    zxvision_draw_line(w,xactual,yactual,xfinal,yfinal,ESTILO_GUI_TINTA_NORMAL,menu_realtape_record_input_draw_waveform_putpixel);
    xactual=xfinal;
    yactual=yfinal;

    //bajo 1
    //util_move_turtle(xactual,yactual,270-angulo_rotacion,1,&xfinal,&yfinal);
    //zxvision_draw_line(w,xactual,yactual,xfinal,yfinal,ESTILO_GUI_TINTA_NORMAL,menu_realtape_record_input_draw_waveform_putpixel);
    yactual++;

    //A la izquierda, aprovechando donde estabamos, como si fuera una impresora matricial
    util_move_turtle(xactual,yactual,180-angulo_rotacion,ancho_lectura_cabezal,&xfinal,&yfinal);
    zxvision_draw_line(w,xactual,yactual,xfinal,yfinal,ESTILO_GUI_TINTA_NORMAL,menu_realtape_record_input_draw_waveform_putpixel);
    xactual=xfinal;
    yactual=yfinal;

    //Tornillo de la derecha
    xactual=origen_x-DRAW_TAPE_ANCHO_TORNILLO*2;
    yactual=origen_y-DRAW_TAPE_ALTO_TORNILLO;
    menu_realtape_record_input_draw_tape_tornillo(w,xactual,yactual,DRAW_TAPE_ALTO_TORNILLO,ESTILO_GUI_TINTA_NORMAL);

    //Tornillo de la izquierda. la y viene de donde esta la base
    xactual=origen_x-ancho_base+(DRAW_TAPE_ANCHO_TORNILLO);//-DRAW_TAPE_ANCHO_TORNILLO*2;
    yactual=yfinalbase-DRAW_TAPE_ALTO_TORNILLO;

    int alto_tornillo=origen_y-yactual;

    int color_tornillo_izq=ESTILO_GUI_TINTA_NORMAL;
    if (menu_realtape_record_input_aviso_azimuth) color_tornillo_izq=ESTILO_GUI_COLOR_AVISO;
    menu_realtape_record_input_draw_tape_tornillo(w,xactual,yactual,alto_tornillo,color_tornillo_izq);


    //Dibujar la linea de cinta
    //Situarnos en la izquierda
    int ancho_cinta=270;
    int origen_x_cinta=origen_x-ancho_base/2; //en el medio
    origen_x_cinta -= ancho_cinta/2;


    int separacion_cabezal_cinta_y=DRAW_TAPE_ALTO_CABEZAL/5;
    int origen_y_cinta=origen_y-separacion_cabezal_cinta_y;

    //La de abajo
    zxvision_draw_line(w,origen_x_cinta,origen_y_cinta,origen_x_cinta+ancho_cinta,origen_y_cinta,ESTILO_GUI_TINTA_NORMAL,menu_realtape_record_input_draw_waveform_putpixel);


    origen_y_cinta=origen_y-DRAW_TAPE_ALTO_CABEZAL+DRAW_TAPE_SEPARACION_CABEZAL_CINTA_Y;

    //La de arriba
    zxvision_draw_line(w,origen_x_cinta,origen_y_cinta,origen_x_cinta+ancho_cinta,origen_y_cinta,ESTILO_GUI_TINTA_NORMAL,menu_realtape_record_input_draw_waveform_putpixel);

    //puntitos
    int xpuntitos=origen_x_cinta+(ancho_cinta/4)*3;
    //int xpuntitos=origen_x_cinta+ancho_cinta;
    int ypuntitos=origen_y_cinta+DRAW_TAPE_SEPARACION_CABEZAL_CINTA_Y+5;

    //borrar el anterior
    if (menu_realtape_record_input_frame_puntitos>0) {
        menu_realtape_record_input_draw_tape_puntitos(w,menu_realtape_record_input_frame_puntitos-1,ESTILO_GUI_PAPEL_NORMAL,xpuntitos,ypuntitos);
    }
    menu_realtape_record_input_draw_tape_puntitos(w,menu_realtape_record_input_frame_puntitos,ESTILO_GUI_TINTA_NORMAL,xpuntitos,ypuntitos);

}

void menu_realtape_record_input_draw_tape(zxvision_window *w)
{

    int angulo_rotacion;


    //Ajuste angulo segun porcentaje exactitud azimuth
    //Si 100% correcto, angulo (realmente desplazamiento angulo) es 0
    //Para facilitar los calculos, tendremos maximo 45 grados de angulo
    if (menu_realtape_record_input_porcentaje_azimuth>=100) angulo_rotacion=0;
    else {

        angulo_rotacion=(100-menu_realtape_record_input_porcentaje_azimuth)/3;
        if (angulo_rotacion>20) angulo_rotacion=20;
    }

    int linea_mostrar_contenido_cinta=DRAW_TAPE_MOSTRAR_CONTENIDO_CINTA_LINEA;


    //coordenada x donde esta la derecha de la base del cabezal
    int origen_x=200;
    //coordenada y donde esta la derecha de la base del cabezal

    int origen_y=(linea_mostrar_contenido_cinta+1)*menu_char_height;

    origen_y+=DRAW_TAPE_SEPARACION_CABEZAL_CINTA_Y;

    menu_realtape_record_input_draw_tape_aux(w,angulo_rotacion,origen_x,origen_y);

}

void menu_realtape_record_input_overlay(void)
{

    menu_speech_set_tecla_pulsada(); //Si no, envia continuamente todo ese texto a speech

    //si ventana minimizada, no ejecutar todo el codigo de overlay
    if (menu_realtape_record_input_window->is_minimized) return;


    int linea=0;

/* Texto salida. Desde linea 0
record buffer
volume
recording to file
Guessed zx
signal type
azimuth accuracy
line wave type
*/

    //menu_realtape_record_input_analize_buffer(menu_realtape_record_input_window);
    linea=menu_realtape_record_input_show_info(menu_realtape_record_input_window,linea);

    menu_realtape_record_input_analize_azimuth_init();
    analizador_espectro_reset();


    int linea_info_azimuth=linea;

    //Saltamos 5 lineas
    //volume
    //recording to file
    //guessed zx
    //signal type
    //azimuth accuracy
    linea+=5;

    //Print....
    //Tambien contar si se escribe siempre o se tiene en cuenta contador_segundo...


    //Forzar a mostrar atajos
    //z80_bit antes_menu_writing_inverse_color;
    //antes_menu_writing_inverse_color.v=menu_writing_inverse_color.v;
    //menu_writing_inverse_color.v=1;

    //menu_realtape_record_input_tipo_onda
    //0=linea continua, 1=relleno, 2=solo puntos
    char tipo_onda[32];
    if (menu_realtape_record_input_tipo_onda==1) strcpy(tipo_onda,"Fill");
    else if (menu_realtape_record_input_tipo_onda==2) strcpy(tipo_onda,"Dots");
    else strcpy(tipo_onda,"Line");


    zxvision_print_string_defaults_fillspc_format(menu_realtape_record_input_window,1,linea++,"[%s] Wave ~~type. [%c] ~~freeze. sh+e: empty buffer",
        tipo_onda,(menu_realtape_record_input_onda_onda_congelada ? 'X' : ' '));


    //Restaurar comportamiento atajos
    //menu_writing_inverse_color.v=antes_menu_writing_inverse_color.v;

    int alto=(menu_realtape_record_input_window->visible_height-2)*menu_char_height;
    int ancho=(menu_realtape_record_input_window->visible_width-2)*menu_char_width;

    int x=1*menu_char_width;
    int y=0;

    //6 lineas de estado y el trozo del dibujo de la cinta
    //int restar_lineas=6*menu_char_height;

    int restar_lineas=(DRAW_TAPE_MOSTRAR_CONTENIDO_CINTA_LINEA+2)*menu_char_height;


    y+=restar_lineas;
    alto-=restar_lineas;


    if (alto>0) {
        //-1 porque si no, se corta 1 pixel por debajo
        menu_realtape_record_input_draw_waveform(menu_realtape_record_input_window,x,y,ancho,alto-1);
    }

    menu_realtape_record_input_analize_azimuth_end(menu_realtape_record_input_window,linea_info_azimuth);



	//esto hara ejecutar esto 2 veces por segundo
	if ( ((contador_segundo%500) == 0 && menu_realtape_record_input_overlay_segundo_anterior!=contador_segundo) || menu_multitarea==0) {

		menu_realtape_record_input_overlay_segundo_anterior=contador_segundo;

        //Hacer decaer indicador de volumen
        menu_realtape_record_input_analize_previo_volumen=menu_decae_dec_valor_volumen(menu_realtape_record_input_analize_previo_volumen,menu_realtape_record_input_analize_volumen_escalado);

    }

    if (audio_is_recording_input) menu_realtape_record_input_frame_puntitos++;

    menu_realtape_record_input_draw_tape(menu_realtape_record_input_window);





    //Mostrar contenido
    zxvision_draw_window_contents(menu_realtape_record_input_window);

}




//Almacenar la estructura de ventana aqui para que se pueda referenciar desde otros sitios
zxvision_window zxvision_window_realtape_record_input;

void menu_realtape_empty_buffer(void)
{
    if (audio_can_record_input() ) {

        if (audio_is_recording_input) {
            //La fifo de entrada
            audiorecord_input_empty_buffer_with_lock();

            //Y tambien este buffer visual
            menu_realtape_record_input_empty_visual_buffer();

            //Y decir que buffer no esta lleno
            audiorecord_last_write_full=0;

        }

    }

}

void menu_realtape_record_input(MENU_ITEM_PARAMETERS)
{
	menu_espera_no_tecla();

    if (!menu_multitarea) {
        menu_warn_message("This window needs multitask enabled");
        return;
    }

    zxvision_window *ventana;
    ventana=&zxvision_window_realtape_record_input;

	//IMPORTANTE! no crear ventana si ya existe. Esto hay que hacerlo en todas las ventanas que permiten background.
	//si no se hiciera, se crearia la misma ventana, y en la lista de ventanas activas , al redibujarse,
	//la primera ventana repetida apuntaria a la segunda, que es el mismo puntero, y redibujaria la misma, y se quedaria en bucle colgado
	//zxvision_delete_window_if_exists(ventana);

    //Crear ventana si no existe
    if (!zxvision_if_window_already_exists(ventana)) {
        int xventana,yventana,ancho_ventana,alto_ventana,is_minimized,is_maximized,ancho_antes_minimize,alto_antes_minimize;

        if (!util_find_window_geometry("externalaudiosource",&xventana,&yventana,&ancho_ventana,&alto_ventana,&is_minimized,&is_maximized,&ancho_antes_minimize,&alto_antes_minimize)) {
            ancho_ventana=55;
            alto_ventana=27;

            xventana=menu_center_x()-ancho_ventana/2;
            yventana=menu_center_y()-alto_ventana/2;
        }


        zxvision_new_window_gn_cim(ventana,xventana,yventana,ancho_ventana,alto_ventana,ancho_ventana-1,alto_ventana-2,"External Audio Source",
            "externalaudiosource",is_minimized,is_maximized,ancho_antes_minimize,alto_antes_minimize);

        ventana->can_be_backgrounded=1;

        //Forzar visibles hotkeys en esa ventana
        ventana->writing_inverse_color=1;

    }

    //Si ya existe, activar esta ventana
    else {
        zxvision_activate_this_window(ventana);
    }

	zxvision_draw_window(ventana);

	z80_byte tecla;


	int salir=0;


    menu_realtape_record_input_window=ventana; //Decimos que el overlay lo hace sobre la ventana que tenemos aqui


    //cambio overlay
    zxvision_set_window_overlay(ventana,menu_realtape_record_input_overlay);


    //Toda ventana que este listada en zxvision_known_window_names_array debe permitir poder salir desde aqui
    //Se sale despues de haber inicializado overlay y de cualquier otra variable que necesite el overlay
    if (zxvision_currently_restoring_windows_on_start) {
            //printf ("Saliendo de ventana ya que la estamos restaurando en startup\n");
            return;
    }

    do {


		tecla=zxvision_common_getkey_refresh();
        //menu_muestra_pending_error_message

        switch (tecla) {

            case 't':
                menu_realtape_record_input_tipo_onda++;
                if (menu_realtape_record_input_tipo_onda>2) menu_realtape_record_input_tipo_onda=0;

            break;


            case 'f':
                menu_realtape_record_input_onda_onda_congelada ^=1;
            break;

            case 'E':
                if (audio_is_recording_input) {
                    menu_realtape_empty_buffer();

                    //Evito splash, porque si mientras se visualiza pulso con el raton fuera,
                    //no cierra el menu, solo la ventana pierde el foco
                    //dejando la ventana de record activa (aunque sin foco) y al pulsar
                    //en ventana de spectrum las teclas shift+space (y esta la opcion de zxvision->use speccy keys)
                    //me cerraria la ventana de record input
                    //menu_generic_message_splash("Empty buffer","Buffer has been emptied");


                    menu_generic_message("Empty buffer","Buffer has been emptied");
                }
            break;

            case 'e':
                if (audio_can_record_input() ) {
                    if (!audio_is_recording_input) {
                        //printf("start recording\n");
                        audiodriver_start_record_input();
                    }
                }
            break;


            //Salir con ESC
            case 2:
                salir=1;
            break;

            //O tecla background
            case 3:
                salir=1;
            break;
        }


    } while (salir==0);


	util_add_window_geometry_compact(ventana);

	if (tecla==3) {
		zxvision_message_put_window_background();
	}

	else {

		zxvision_destroy_window(ventana);
	}


}






zxvision_window *menu_input_spectrum_analyzer_window;


void menu_input_spectrum_analyzer_overlay(void)
{

    menu_speech_set_tecla_pulsada(); //Si no, envia continuamente todo ese texto a speech

    //si ventana minimizada, no ejecutar todo el codigo de overlay
    if (menu_input_spectrum_analyzer_window->is_minimized) return;

    zxvision_window *w=menu_input_spectrum_analyzer_window;


    //Print....
    //Tambien contar si se escribe siempre o se tiene en cuenta contador_segundo...

    zxvision_window *ventana_buscar=zxvision_find_window_in_background("externalaudiosource");

    if (ventana_buscar==NULL) {
        zxvision_cls(w);
        zxvision_print_string_defaults(w,1,0,"You need to have External Audio Source Window open");
        zxvision_print_string_defaults(w,1,1,"in order to have Spectrum Analyzer working");
    }

    else {
        int linea=0;
        char buffer_tipo[30];
        if (analizador_spectro_tipo == 1) strcpy(buffer_tipo,"Percentage");
        else if (analizador_spectro_tipo == 0) strcpy(buffer_tipo,"Volume");
        else strcpy(buffer_tipo,"Volume*Percentage");

        //Forzar a mostrar atajos
        z80_bit antes_menu_writing_inverse_color;
        antes_menu_writing_inverse_color.v=menu_writing_inverse_color.v;
        menu_writing_inverse_color.v=1;

        zxvision_print_string_defaults_fillspc_format(w,1,linea++,"[%s] View ~~type",buffer_tipo);

        //Restaurar comportamiento atajos
        menu_writing_inverse_color.v=antes_menu_writing_inverse_color.v;


        analizador_espectro_muestra_resultados(menu_input_spectrum_analyzer_window,linea);
    }


    //Mostrar contenido
    zxvision_draw_window_contents(menu_input_spectrum_analyzer_window);

}




//Almacenar la estructura de ventana aqui para que se pueda referenciar desde otros sitios
zxvision_window zxvision_window_input_spectrum_analyzer;


void menu_input_spectrum_analyzer(MENU_ITEM_PARAMETERS)
{
	menu_espera_no_tecla();

    if (!menu_multitarea) {
        menu_warn_message("This window needs multitask enabled");
        return;
    }

    zxvision_window *ventana;
    ventana=&zxvision_window_input_spectrum_analyzer;

	//IMPORTANTE! no crear ventana si ya existe. Esto hay que hacerlo en todas las ventanas que permiten background.
	//si no se hiciera, se crearia la misma ventana, y en la lista de ventanas activas , al redibujarse,
	//la primera ventana repetida apuntaria a la segunda, que es el mismo puntero, y redibujaria la misma, y se quedaria en bucle colgado
	//zxvision_delete_window_if_exists(ventana);

    //Crear ventana si no existe
    if (!zxvision_if_window_already_exists(ventana)) {
        int xventana,yventana,ancho_ventana,alto_ventana,is_minimized,is_maximized,ancho_antes_minimize,alto_antes_minimize;

        if (!util_find_window_geometry("inspectrumanalyzer",&xventana,&yventana,&ancho_ventana,&alto_ventana,&is_minimized,&is_maximized,&ancho_antes_minimize,&alto_antes_minimize)) {
            ancho_ventana=38;
            alto_ventana=36;

            xventana=menu_center_x()-ancho_ventana/2;
            yventana=menu_center_y()-alto_ventana/2;
        }


        zxvision_new_window_gn_cim(ventana,xventana,yventana,ancho_ventana,alto_ventana,ancho_ventana-1,alto_ventana-2,"Input Spectrum Analyzer",
            "inspectrumanalyzer",is_minimized,is_maximized,ancho_antes_minimize,alto_antes_minimize);

        ventana->can_be_backgrounded=1;

    }

    //Si ya existe, activar esta ventana
    else {
        zxvision_activate_this_window(ventana);
    }

	zxvision_draw_window(ventana);

	z80_byte tecla;


	int salir=0;


    menu_input_spectrum_analyzer_window=ventana; //Decimos que el overlay lo hace sobre la ventana que tenemos aqui


    //cambio overlay
    zxvision_set_window_overlay(ventana,menu_input_spectrum_analyzer_overlay);


    //Toda ventana que este listada en zxvision_known_window_names_array debe permitir poder salir desde aqui
    //Se sale despues de haber inicializado overlay y de cualquier otra variable que necesite el overlay
    if (zxvision_currently_restoring_windows_on_start) {
            //printf ("Saliendo de ventana ya que la estamos restaurando en startup\n");
            return;
    }

    do {


		tecla=zxvision_common_getkey_refresh();


        switch (tecla) {


            case 't':
                analizador_spectro_tipo++;
                if (analizador_spectro_tipo>2) analizador_spectro_tipo=0;
            break;


            //Salir con ESC
            case 2:
                salir=1;
            break;

            //O tecla background
            case 3:
                salir=1;
            break;
        }


    } while (salir==0);


	util_add_window_geometry_compact(ventana);

	if (tecla==3) {
		zxvision_message_put_window_background();
	}

	else {

		zxvision_destroy_window(ventana);
	}


}





void menu_generic_visualtape_dibujar_un_rollo(struct zxvision_vectorial_draw *d,int radio,int dibujar_borrar)
{
    int max_radio=GENERIC_VISUALTAPE_ROLLO_MAX_RADIO; //para un maximo de una cinta de 90
    int min_radio=GENERIC_VISUALTAPE_ROLLO_MIN_RADIO;

    int color_fondo=GENERIC_VISUALTAPE_COLOR_FONDO;

    int color_cinta_enrollada=0; //esto sera negro

    int i;

    d->setcolour(d,color_fondo);

    //borrar lo que excede

    if (!dibujar_borrar) {
        for (i=radio;i<=max_radio;i++) {
            d->drawcircle(d,i);
        }
    }

    //dibujar lo que aplica

    if (dibujar_borrar) {
        d->setcolour(d,color_cinta_enrollada);

        for (i=min_radio;i<=radio;i++) {
            d->drawcircle(d,i);
        }
    }
}



//Retorna el radio de los rollos segun porcentajes
void menu_generic_visualtape_da_radios_rollos(int porcentaje_cinta_izquierdo,int porcentaje_cinta_derecha,int *p_radio_izq,int *p_radio_der)
{
    int max_radio=GENERIC_VISUALTAPE_ROLLO_MAX_RADIO;
    int min_radio=GENERIC_VISUALTAPE_ROLLO_MIN_RADIO;

    //calcular que tanto de radio relleno
    int sumar_radio_izq=((max_radio-min_radio)*porcentaje_cinta_izquierdo)/100;
    int sumar_radio_der=((max_radio-min_radio)*porcentaje_cinta_derecha)/100;

    int radio_rellenar_izquierdo=min_radio+sumar_radio_izq;
    int radio_rellenar_derecho=min_radio+sumar_radio_der;

    *p_radio_izq=radio_rellenar_izquierdo;
    *p_radio_der=radio_rellenar_derecho;

}

void menu_generic_visualtape_dibujar_rollos(struct zxvision_vectorial_draw *d,int porcentaje_cinta_izquierdo,int porcentaje_cinta_derecha)
{



    int alto_cinta=GENERIC_VISUALTAPE_ALTO_CINTA;
    int color_marco=VISUALTAPE_COLOR_MARCO; //gris


    //calcular que tanto de radio relleno


    int radio_rellenar_izquierdo;
    int radio_rellenar_derecho;

    menu_generic_visualtape_da_radios_rollos(porcentaje_cinta_izquierdo,porcentaje_cinta_derecha,&radio_rellenar_izquierdo,&radio_rellenar_derecho);

    //Borrar
    d->pencil_off(d);
    d->setpos(d,VISUALTAPE_RODILLO_ARRASTRE_IZQ_X,VISUALTAPE_RODILLO_ARRASTRE_Y);
    menu_generic_visualtape_dibujar_un_rollo(d,radio_rellenar_izquierdo,0);

    d->pencil_off(d);
    d->setpos(d,GENERIC_VISUALTAPE_ROLLO_DERECHO_X,VISUALTAPE_RODILLO_ARRASTRE_Y);
    menu_generic_visualtape_dibujar_un_rollo(d,radio_rellenar_derecho,0);


    //Dibujar
    d->pencil_off(d);
    d->setpos(d,VISUALTAPE_RODILLO_ARRASTRE_IZQ_X,VISUALTAPE_RODILLO_ARRASTRE_Y);
    menu_generic_visualtape_dibujar_un_rollo(d,radio_rellenar_izquierdo,1);

    d->pencil_off(d);
    d->setpos(d,GENERIC_VISUALTAPE_ROLLO_DERECHO_X,VISUALTAPE_RODILLO_ARRASTRE_Y);
    menu_generic_visualtape_dibujar_un_rollo(d,radio_rellenar_derecho,1);

    //Paralelogramo de abajo
    d->pencil_on(d);
    d->setcolour(d,color_marco);
    d->jumppos(d,150,alto_cinta-1);
    d->setrelpos(d,+45,-160);
    d->setrelpos(d,+610,0);
    d->setrelpos(d,+45,+160);


}



void menu_generic_visualtape_dibujar_cinta_estatica(struct zxvision_vectorial_draw *d,int pestanyas_escritura)
{
    int radio_bordes_esquinas=30;

    int ancho_cinta=GENERIC_VISUALTAPE_ANCHO_CINTA;
    int alto_cinta=GENERIC_VISUALTAPE_ALTO_CINTA;
    int color_marco=VISUALTAPE_COLOR_MARCO;


    d->pencil_on(d);
    d->setcolour(d,color_marco);

    //Marco exterior de la cinta
    d->jumppos(d,radio_bordes_esquinas,0);
    d->set_x(d,ancho_cinta-1-radio_bordes_esquinas);

    d->jumppos(d,ancho_cinta,radio_bordes_esquinas);
    d->set_y(d,alto_cinta-1-radio_bordes_esquinas);

    d->jumppos(d,ancho_cinta-1-radio_bordes_esquinas,alto_cinta-1);
    d->set_x(d,radio_bordes_esquinas);

    d->jumppos(d,0,alto_cinta-1-radio_bordes_esquinas);
    d->set_y(d,radio_bordes_esquinas);

    //Esquina arriba izquierda
    d->jumppos(d,radio_bordes_esquinas,radio_bordes_esquinas);
    d->drawarc(d,radio_bordes_esquinas,90,180);
    //Arriba derecha
    d->jumppos(d,ancho_cinta-radio_bordes_esquinas,radio_bordes_esquinas);
    d->drawarc(d,radio_bordes_esquinas,0,90);
    //Abajo derecha
    d->jumppos(d,ancho_cinta-radio_bordes_esquinas,alto_cinta-radio_bordes_esquinas);
    d->drawarc(d,radio_bordes_esquinas,270,361); //con 360 no acaba de cerrar bien el marco
    //Abajo izquierda
    d->jumppos(d,radio_bordes_esquinas,alto_cinta-radio_bordes_esquinas);
    d->drawarc(d,radio_bordes_esquinas,180,270);

    //Rodillos. Parte blanca
    int max_radio=VISUALTAPE_RODILLO_ARRASTRE_MAX_RADIO;
    int min_radio=VISUALTAPE_RODILLO_ARRASTRE_MIN_RADIO;


    int i;
    d->setcolour(d,VISUALTAPE_COLOR_RODILLOS);

    for (i=min_radio;i<=max_radio;i++) {
        d->jumppos(d,VISUALTAPE_RODILLO_ARRASTRE_IZQ_X,VISUALTAPE_RODILLO_ARRASTRE_Y);
        d->drawcircle(d,i);
        d->jumppos(d,VISUALTAPE_RODILLO_ARRASTRE_DER_X,VISUALTAPE_RODILLO_ARRASTRE_Y);
        d->drawcircle(d,i);
    }

    //rodillos fijo donde se ancla la cinta
    d->setcolour(d,VISUALTAPE_COLOR_RODILLOS);

    for (i=0;i<=VISUALTAPE_RODILLO_FIJO_RADIO;i++) {
        d->jumppos(d,VISUALTAPE_RODILLO_FIJO_IZQUIERDO_X,VISUALTAPE_RODILLO_FIJO_Y);
        d->drawcircle(d,i);
        d->jumppos(d,VISUALTAPE_RODILLO_FIJO_DERECHO_X,VISUALTAPE_RODILLO_FIJO_Y);
        d->drawcircle(d,i);
    }

    //rodillos inferiores moviles. Aunque yo no los hago moviles
    for (i=VISUALTAPE_RODILLO_MOVIL_RADIO_INTERIOR;i<=VISUALTAPE_RODILLO_MOVIL_RADIO;i++) {
        d->jumppos(d,VISUALTAPE_RODILLO_MOVIL_IZQUIERDO_X,VISUALTAPE_RODILLO_MOVIL_Y);
        d->drawcircle(d,i);
        d->jumppos(d,VISUALTAPE_RODILLO_MOVIL_DERECHO_X,VISUALTAPE_RODILLO_MOVIL_Y);
        d->drawcircle(d,i);
    }

    //la parte interior en negro
    d->setcolour(d,0);
    for (i=0;i<=VISUALTAPE_RODILLO_MOVIL_RADIO_INTERIOR;i++) {
        d->jumppos(d,VISUALTAPE_RODILLO_MOVIL_IZQUIERDO_X,VISUALTAPE_RODILLO_MOVIL_Y);
        d->drawcircle(d,i);
        d->jumppos(d,VISUALTAPE_RODILLO_MOVIL_DERECHO_X,VISUALTAPE_RODILLO_MOVIL_Y);
        d->drawcircle(d,i);
    }

    //si no tiene pestañas, ponerlas del color del fondo, para borrarlas
    if (!pestanyas_escritura) {
        d->setcolour(d,GENERIC_VISUALTAPE_COLOR_FONDO);
        d->jumppos(d,60,0);
        //d->drawfilledrectangle(d,60,10);
        d->setrelpos(d,+60,0);

        d->jumppos(d,GENERIC_VISUALTAPE_ANCHO_CINTA-60-60,0);
        //d->drawfilledrectangle(d,60,10);
        d->setrelpos(d,+60,0);
    }


}

int menu_generic_visualtap_calculate_coord_grados(int sine,int longitud,int grados,int real_width,int virtual_width)
{
    //sacar final linea
    int longitud_y;

    if (sine) {
        longitud_y=(longitud*util_get_sine(grados))/10000;
    }
    else {
        longitud_y=(longitud*util_get_cosine(grados))/10000;
    }
    //printf("longitud_y antes: %d\n",longitud_y);

    //reajustar comportamiento decimales para que se comporte como el dibujado de circulos
    //Nota: eso es debido a que el calculo de coordenadas virtuales se hace diferente al dibujar el circulo o al trazar una linea
    //y/o tambien a la falta de uso de variables con decimales para calcular
    //probablemente con el uso de float para real_radio en zxvision_vecdraw_arc se solventaria

    //Si hacemos paso de coordenadas virtuales a reales, y de vuelta a virtuales,
    //en el calculo sin decimales nos comportaremos ¿igual? que el dibujado de circulos
    //Si no hiciera esto, estas lineas de radio no están siempre exactas desde el centro al circulo,
    //a veces sobresale del circulo (cuando el dibujo es pequeño), a veces no llega a tocar al circulo (cuando el dibujo es grande)

    //pasar a dimensiones reales
    if (virtual_width==0) longitud_y=0;
    else longitud_y=(longitud_y*real_width)/virtual_width;

    //y de vuelta a virtuales
    if (real_width==0) longitud_y=0;
    else longitud_y=(longitud_y*virtual_width)/real_width;

    return longitud_y;

}

//Dibujar rodillos de arrastre con su movimiento, asi como cinta que une la zona enrollada con rodillos pequeños fijos
void menu_generic_visualtape_dibujar_rodillo_arrastre(struct zxvision_vectorial_draw *d,
    int x_origen_rodillo,int y_origen_rodillo,int longitud,int longitud2,int grados,int color)
{


    d->pencil_off(d);
    d->setcolour(d,color);
    d->setpos(d,x_origen_rodillo,y_origen_rodillo);
    d->pencil_on(d);



    //sacar final linea
    int longitud_y=menu_generic_visualtap_calculate_coord_grados(1,longitud,grados,d->real_width,d->virtual_width);
    int yfinal=y_origen_rodillo-longitud_y;


    int longitud_x=menu_generic_visualtap_calculate_coord_grados(0,longitud,grados,d->real_heigth,d->virtual_height);
    int xfinal=x_origen_rodillo+longitud_x;


    d->jumppos(d,xfinal,yfinal);


    longitud_y=menu_generic_visualtap_calculate_coord_grados(1,longitud2,grados,d->real_width,d->virtual_width);
    yfinal=yfinal-longitud_y;


    longitud_x=menu_generic_visualtap_calculate_coord_grados(0,longitud2,grados,d->real_heigth,d->virtual_height);
    xfinal=xfinal+longitud_x;


    d->setpos(d,xfinal,yfinal);


}

void menu_generic_visualtape_cinta_rollos_rodillos(struct zxvision_vectorial_draw *d,
    int porcentaje_cinta_izquierdo,int porcentaje_cinta_derecha,int color,int temblor)
{
    //Linea que une el rodillo enrollado hasta los rodillos pequeños fijos y a los moviles de abajo

    int radio_rellenar_izquierdo;
    int radio_rellenar_derecho;

    menu_generic_visualtape_da_radios_rollos(porcentaje_cinta_izquierdo,porcentaje_cinta_derecha,&radio_rellenar_izquierdo,&radio_rellenar_derecho);

    //Algo menos de radio para que no parezca que la cinta no toca el rodillo
    radio_rellenar_izquierdo-=2;
    radio_rellenar_derecho-=2;

    //Efecto temblor
    if (temblor) {
        radio_rellenar_izquierdo -=2;
        radio_rellenar_derecho -=2;
    }

    //Izquierdo
    int pos_x_origen=VISUALTAPE_RODILLO_ARRASTRE_IZQ_X-radio_rellenar_izquierdo;
    int pos_y_origen=VISUALTAPE_RODILLO_ARRASTRE_Y;

    d->jumppos(d,pos_x_origen,pos_y_origen);
    int color_cinta=color;
    d->setcolour(d,color_cinta);

    //Calcular final rollo
    d->pencil_on(d);
    d->setpos(d,VISUALTAPE_RODILLO_FIJO_IZQUIERDO_X-VISUALTAPE_RODILLO_FIJO_RADIO,VISUALTAPE_RODILLO_FIJO_Y);

    //Y ahora hasta el rodillo movil
    d->setpos(d,VISUALTAPE_RODILLO_MOVIL_IZQUIERDO_X-VISUALTAPE_RODILLO_MOVIL_RADIO,VISUALTAPE_RODILLO_MOVIL_Y);

    //Derecho
    pos_x_origen=GENERIC_VISUALTAPE_ROLLO_DERECHO_X+radio_rellenar_derecho;
    pos_y_origen=VISUALTAPE_RODILLO_ARRASTRE_Y;

    d->jumppos(d,pos_x_origen,pos_y_origen);

    //Calcular final rollo
    d->setpos(d,VISUALTAPE_RODILLO_FIJO_DERECHO_X+VISUALTAPE_RODILLO_FIJO_RADIO,VISUALTAPE_RODILLO_FIJO_Y);

    //Y ahora hasta el rodillo movil
    d->setpos(d,VISUALTAPE_RODILLO_MOVIL_DERECHO_X+VISUALTAPE_RODILLO_MOVIL_RADIO,VISUALTAPE_RODILLO_MOVIL_Y);

    //Y cinta que une por debajo
    d->jumppos(d,VISUALTAPE_RODILLO_MOVIL_IZQUIERDO_X,VISUALTAPE_RODILLO_MOVIL_Y+VISUALTAPE_RODILLO_MOVIL_RADIO);
    d->setpos(d,VISUALTAPE_RODILLO_MOVIL_DERECHO_X,VISUALTAPE_RODILLO_MOVIL_Y+VISUALTAPE_RODILLO_MOVIL_RADIO);

}

void menu_generic_visualtape_draw_tdk(struct zxvision_vectorial_draw *d)
{

    int inicio_letras_x=GENERIC_VISUALTAPE_ANCHO_CINTA/2-75;
    int inicio_letras_y=GENERIC_VISUALTAPE_ALTO_CINTA-200;
    int alto_letras=50;

    d->setcolour(d,7);
    d->pencil_on(d);

    //T
    d->jumppos(d,inicio_letras_x+25,inicio_letras_y);
    d->setrelpos(d,0,-alto_letras);
    d->jumppos(d,inicio_letras_x+5,inicio_letras_y-alto_letras);
    d->setrelpos(d,+40,0);

    //D
    d->jumppos(d,inicio_letras_x+50+5,inicio_letras_y);
    d->setrelpos(d,0,-alto_letras);
    d->setrelpos(d,+15,0);
    d->jumppos(d,inicio_letras_x+50+5,inicio_letras_y);
    d->setrelpos(d,+15,0);
    d->jumppos(d,inicio_letras_x+50+5+15,inicio_letras_y-alto_letras/2);
    d->drawarc(d,alto_letras/2,269,360+91);

    //K
    d->jumppos(d,inicio_letras_x+100+5,inicio_letras_y);
    d->setrelpos(d,0,-alto_letras);
    d->jumppos(d,inicio_letras_x+100+5,inicio_letras_y-alto_letras/2);
    d->setrelpos(d,+30,-alto_letras/2);
    d->jumppos(d,inicio_letras_x+100+5,inicio_letras_y-alto_letras/2);
    d->setrelpos(d,+30,+alto_letras/2);

}

void menu_generic_visualtape_draw_rodillos_arrastre(struct zxvision_vectorial_draw *d,
    int grados_rodillos,int antes_grados_rodillos,int porcentaje_cinta_izquierdo,int porcentaje_cinta_derecha,
    int antes_porcentaje_cinta_izquierdo,int antes_porcentaje_cinta_derecha,int temblor)
{


    int color_rodillo=VISUALTAPE_COLOR_RODILLOS;
    int centro_rodillo_x=VISUALTAPE_RODILLO_ARRASTRE_IZQ_X;
    int centro_rodillo_dos_x=GENERIC_VISUALTAPE_ROLLO_DERECHO_X;
    int centro_rodillo_y=VISUALTAPE_RODILLO_ARRASTRE_Y;

    int longitud2=20;

    //El dentado consiste en trazar desde el centro hasta tocar el rodillo,
    //pero con longitud2 de longitud de cada diente

    //radio interior del rodillo=110/2
    int longitud=VISUALTAPE_RODILLO_ARRASTRE_MIN_RADIO-longitud2;

    //primero borrar los anteriores
    int color_fondo=GENERIC_VISUALTAPE_COLOR_FONDO;
    int grados=antes_grados_rodillos;
    int segmentos;

    for (segmentos=0;segmentos<6;segmentos++) {
        menu_generic_visualtape_dibujar_rodillo_arrastre(d,centro_rodillo_x,centro_rodillo_y,
        longitud,longitud2,grados,color_fondo);
        menu_generic_visualtape_dibujar_rodillo_arrastre(d,centro_rodillo_dos_x,centro_rodillo_y,
        longitud,longitud2,grados,color_fondo);

        grados +=60;
    }

    grados=grados_rodillos;


    for (segmentos=0;segmentos<6;segmentos++) {
        menu_generic_visualtape_dibujar_rodillo_arrastre(d,centro_rodillo_x,centro_rodillo_y,
        longitud,longitud2,grados,color_rodillo);
        menu_generic_visualtape_dibujar_rodillo_arrastre(d,centro_rodillo_dos_x,centro_rodillo_y,
        longitud,longitud2,grados,color_rodillo);

        grados +=60;
    }

    //Linea que une el rodillo enrollado hasta los rodillos pequeños fijos. Primero borrar con porcentaje anterior
    menu_generic_visualtape_cinta_rollos_rodillos(d,antes_porcentaje_cinta_izquierdo,antes_porcentaje_cinta_derecha,
        GENERIC_VISUALTAPE_COLOR_FONDO,temblor^1);

    //Luego dibujar
    menu_generic_visualtape_cinta_rollos_rodillos(d,porcentaje_cinta_izquierdo,porcentaje_cinta_derecha,
        0,temblor);



    menu_generic_visualtape_draw_tdk(d);


}

void menu_generic_visualtape_draw_un_semaforo(struct zxvision_vectorial_draw *d,int activo,int color)
{

    int radio_semaforos=30;



    int color_relleno=GENERIC_VISUALTAPE_COLOR_FONDO;
    if (activo) color_relleno=color+8; //activo con brillo
    int i;

    //Rellenar de color o bien de fondo
    for (i=0;i<=radio_semaforos;i++) {
        d->setcolour(d,color_relleno);
        d->drawcircle(d,i);
    }

    //Recuadro exterior, para cuando no esta activo
    if (!activo) {
        d->setcolour(d,color);
        d->drawcircle(d,radio_semaforos);
    }

}

void menu_generic_visualtape_draw_semaforos_hilow(struct zxvision_vectorial_draw *d,int activo_rojo,int activo_amarillo,int activo_verde)
{

    int x_semaforos=1050;

    int y_inicial_semaforos=100;
    int separacion_semaforos=200;

    //borrar o rellenar contenido
    d->jumppos(d,x_semaforos,y_inicial_semaforos);
    menu_generic_visualtape_draw_un_semaforo(d,activo_rojo,2);


    d->jumppos(d,x_semaforos,y_inicial_semaforos+separacion_semaforos);
    menu_generic_visualtape_draw_un_semaforo(d,activo_amarillo,6);


    d->jumppos(d,x_semaforos,y_inicial_semaforos+separacion_semaforos+separacion_semaforos);
    menu_generic_visualtape_draw_un_semaforo(d,activo_verde,4);

}

//Dibujar una cinta vectorial en pantalla
//Parametros:
//w: en que ventana
//porcentaje_cinta_izquierdo: que tanto % de cinta esta llena en el cilindro izquierdo. En el derecho sera 100-porcentaje_cinta_izquierdo
//redibujar_rollos: redibujar cinta enrollada, 0 o 1. Se le pone a 1 cuando ha cambiado significativamente
//redibujar_parte_estatica: si redibujar partes estaticas: marco exterior, recuadros... todo aquello que no es dinamico
//semaforos_hilow: si se dibujan o no, y los activos:
//bit 0: activo o no. Bit 1: si redibujarlos. Bit 2: si activo rojo, bit 3: si activo amarillo, bit 4: si activo verde
void menu_generic_visualtape(zxvision_window *w,
    int real_width,int real_height,int offset_x,int offset_y,
    int porcentaje_cinta_izquierdo,int porcentaje_cinta_derecha,
    int antes_porcentaje_cinta_izquierdo,int antes_porcentaje_cinta_derecha,
    int grados_rodillos,int antes_grados_rodillos,int redibujar_rollos,int redibujar_parte_estatica,int redibujar_rodillos_arrastre,
    int temblor,int semaforos_hilow,int pestanyas_escritura)
{

    //Dibujo de la cinta
    struct zxvision_vectorial_draw dibujo_visualtape;



    int ancho_total_dibujo_virtual=GENERIC_VISUALTAPE_ANCHO_CINTA;

    if (semaforos_hilow) ancho_total_dibujo_virtual+=100;

    zxvision_vecdraw_init(&dibujo_visualtape,w,ancho_total_dibujo_virtual,GENERIC_VISUALTAPE_ALTO_CINTA,
        real_width,real_height,offset_x,offset_y);



    if (redibujar_parte_estatica) menu_generic_visualtape_dibujar_cinta_estatica(&dibujo_visualtape,pestanyas_escritura);
    if (redibujar_rollos) menu_generic_visualtape_dibujar_rollos(&dibujo_visualtape,porcentaje_cinta_izquierdo,porcentaje_cinta_derecha);

    if (redibujar_rodillos_arrastre) {

        //printf("Redibujando rodillos arrastre. %d\n",contador_segundo_infinito);

        menu_generic_visualtape_draw_rodillos_arrastre(&dibujo_visualtape,grados_rodillos,antes_grados_rodillos,
        porcentaje_cinta_izquierdo,porcentaje_cinta_derecha,
        antes_porcentaje_cinta_izquierdo,antes_porcentaje_cinta_derecha,temblor);

    }

    if (semaforos_hilow) {
    //bit 0: activo o no. Bit 1: si redibujarlos. Bit 2: si activo rojo, bit 3: si activo amarillo, bit 4: si activo verde
        if (semaforos_hilow & 2) {
            //printf("redibujando semaforos. %d\n",contador_segundo_infinito);
            int activo_rojo=semaforos_hilow & 4;
            int activo_amarillo=semaforos_hilow & 8;
            int activo_verde=semaforos_hilow & 16;

            menu_generic_visualtape_draw_semaforos_hilow(&dibujo_visualtape,activo_rojo,activo_amarillo,activo_verde);
        }
    }


}




void menu_zxuno_spi_persistent_writes(MENU_ITEM_PARAMETERS)
{
	if (zxuno_flash_persistent_writes.v==0) {
           if (menu_confirm_yesno_texto("Will flush prev. changes","Enable?")==1) zxuno_flash_persistent_writes.v=1;

	}

	else zxuno_flash_persistent_writes.v=0;
}

void menu_zxuno_spi_flash_file(MENU_ITEM_PARAMETERS)
{
	char *filtros[2];

        filtros[0]="flash";
        filtros[1]=0;


        if (menu_filesel("Select Flash File",filtros,zxuno_flash_spi_name)==1) {

                //Ver si archivo existe y preguntar
                /*if (si_existe_archivo(zxuno_flash_spi_name) ) {

                        if (menu_confirm_yesno_texto("File exists","Overwrite?")==0) return;

                }
		*/

		if (si_existe_archivo(zxuno_flash_spi_name) ) {

			if (menu_confirm_yesno_texto("File exists","Reload SPI Flash from file?")) {

				//Decir que no hay que hacer flush anteriores
				zxuno_flash_must_flush_to_disk=0;

				//Y sobreescribir ram spi flash con lo que tiene el archivo de disco
				zxuno_load_spi_flash();
			}

		}

		else {

			//Si archivo nuevo,
			//volcar contenido de la memoria flash en ram aqui
			//Suponemos que permisos de escritura estan activos
			zxuno_flash_must_flush_to_disk=1;
		}

        }

	//Sale con ESC
        else {
		//dejar archivo por defecto
		zxuno_flash_spi_name[0]=0;

		//Y por defecto solo lectura
		zxuno_flash_persistent_writes.v=0;

		if (menu_confirm_yesno_texto("Default SPI Flash file","Reload SPI Flash from file?")) {
			zxuno_load_spi_flash();
                }

        }
}



void menu_zxuno_spi_write_protect(MENU_ITEM_PARAMETERS)
{
	zxuno_flash_write_protection.v ^=1;
}



void menu_zxuno_spi_flash(MENU_ITEM_PARAMETERS)
{
        menu_item *array_menu_zxuno_spi_flash;
        menu_item item_seleccionado;
        int retorno_menu;
        do {

                     char string_spi_flash_file_shown[12]; //,string_mmc_file_shown[13];
			if (zxuno_flash_spi_name[0]==0) sprintf (string_spi_flash_file_shown,"Default");
			else menu_tape_settings_trunc_name(zxuno_flash_spi_name,string_spi_flash_file_shown,12);

			menu_add_item_menu_inicial_format(&array_menu_zxuno_spi_flash,MENU_OPCION_NORMAL,menu_zxuno_spi_flash_file,NULL,"~~Flash File: [%s]",string_spi_flash_file_shown);
            menu_add_item_menu_prefijo(array_menu_zxuno_spi_flash,"    ");
			menu_add_item_menu_shortcut(array_menu_zxuno_spi_flash,'f');
			menu_add_item_menu_tooltip(array_menu_zxuno_spi_flash,"File used for the ZX-Uno SPI Flash");
			menu_add_item_menu_ayuda(array_menu_zxuno_spi_flash,"File used for the ZX-Uno SPI Flash");

			menu_add_item_menu_format(array_menu_zxuno_spi_flash,MENU_OPCION_NORMAL,menu_zxuno_spi_write_protect,NULL,"[%c] ~~Write protect",(zxuno_flash_write_protection.v ? 'X' : ' '));
			menu_add_item_menu_shortcut(array_menu_zxuno_spi_flash,'w');
                        menu_add_item_menu_tooltip(array_menu_zxuno_spi_flash,"If ZX-Uno SPI Flash is write protected");
                        menu_add_item_menu_ayuda(array_menu_zxuno_spi_flash,"If ZX-Uno SPI Flash is write protected");



			menu_add_item_menu_format(array_menu_zxuno_spi_flash,MENU_OPCION_NORMAL,menu_zxuno_spi_persistent_writes,NULL,"[%c] Persistent Writes",(zxuno_flash_persistent_writes.v ? 'X' : ' '));
			menu_add_item_menu_tooltip(array_menu_zxuno_spi_flash,"Tells if ZX-Uno SPI Flash writes are saved to disk");
			menu_add_item_menu_ayuda(array_menu_zxuno_spi_flash,"Tells if ZX-Uno SPI Flash writes are saved to disk. "
			"When you enable it, all previous changes (before enable it and since machine boot) and "
			"future changes made to spi flash will be saved to disk.\n"
			"Note: all writing operations to SPI Flash are always saved to internal memory (unless you disable write permission), but this setting "
			"tells if these changes are written to disk or not.");




				menu_add_item_menu(array_menu_zxuno_spi_flash,"",MENU_OPCION_SEPARADOR,NULL,NULL);
                //menu_add_item_menu(array_menu_zxuno_spi_flash,"ESC Back",MENU_OPCION_NORMAL|MENU_OPCION_ESC,NULL,NULL);
                menu_add_ESC_item(array_menu_zxuno_spi_flash);

                retorno_menu=menu_dibuja_menu_no_title_lang(&zxuno_spi_flash_opcion_seleccionada,&item_seleccionado,array_menu_zxuno_spi_flash,"ZX-Uno Flash" );


                if ((item_seleccionado.tipo_opcion&MENU_OPCION_ESC)==0 && retorno_menu>=0) {
                        //llamamos por valor de funcion
                        if (item_seleccionado.menu_funcion!=NULL) {
                                //printf ("actuamos por funcion\n");
                                item_seleccionado.menu_funcion(item_seleccionado.valor_opcion);

                        }
                }

        } while ( (item_seleccionado.tipo_opcion&MENU_OPCION_ESC)==0 && retorno_menu!=MENU_RETORNO_ESC && !salir_todos_menus);




}

void menu_hardware_transtape_enable(MENU_ITEM_PARAMETERS)
{
    if (transtape_enabled.v) {
        transtape_disable();
    }
    else {
        transtape_enable();
    }
}

void menu_hardware_transtape_switch_saveload(MENU_ITEM_PARAMETERS)
{
    transtape_switch_a10.v ^=1;
}

void menu_hardware_transtape_switch_menu(MENU_ITEM_PARAMETERS)
{
    transtape_switch_a11.v ^=1;
}

void menu_hardware_transtape_version(MENU_ITEM_PARAMETERS)
{
    if (transtape_version==2) transtape_version=3;
    else transtape_version=2;
}

int menu_hardware_transtape_version_cond(void)
{
    if (transtape_enabled.v) return 0;
    else return 1;
}

void menu_hardware_transtape_reset_button(MENU_ITEM_PARAMETERS)
{
    transtape_simulate_reset_button();

     menu_generic_message_splash("Reset button","OK. Reset pressed");
}

void menu_hardware_transtape_romfile(MENU_ITEM_PARAMETERS)
{

    char *filtros[2];

    filtros[0]="rom";
    filtros[1]=0;


    if (menu_filesel("Select ROM File",filtros,transtape_rom_filename)==1) {
        if (!si_existe_archivo(transtape_rom_filename)) {
            menu_error_message("File does not exist");
            transtape_rom_filename[0]=0;
            return;
        }

        else {
            //Comprobar aqui tambien el tamanyo
            long long int size=get_file_size(transtape_rom_filename);
            if (size!=TRANSTAPE_ROM_SIZE) {
                menu_error_message("ROM file is not expected size");
                transtape_rom_filename[0]=0;
                return;
            }
        }

    }
    //Sale con ESC
    else {
        //Quitar nombre
        transtape_rom_filename[0]=0;
    }

}

void menu_transtape(MENU_ITEM_PARAMETERS)
{
    menu_item *array_menu_common;
    menu_item item_seleccionado;
    int retorno_menu;
    do {



        menu_add_item_menu_inicial_format(&array_menu_common,MENU_OPCION_NORMAL,menu_hardware_transtape_enable,
                NULL,"[%c] ~~Transtape Enabled", (transtape_enabled.v ? 'X' : ' '));
        menu_add_item_menu_shortcut(array_menu_common,'t');
        menu_add_item_menu_tooltip(array_menu_common,"Enable transtape");
        menu_add_item_menu_ayuda(array_menu_common,"Enable transtape");

        menu_add_item_menu_en_es_ca(array_menu_common,MENU_OPCION_NORMAL,menu_hardware_transtape_version,menu_hardware_transtape_version_cond,
            "Version","Versión","Versió");
        menu_add_item_menu_prefijo_format(array_menu_common,"[%d] ",transtape_version);

        char string_rom_file_shown[20];
        menu_tape_settings_trunc_name(transtape_rom_filename,string_rom_file_shown,20);


        menu_add_item_menu_en_es_ca(array_menu_common,MENU_OPCION_NORMAL,menu_hardware_transtape_romfile,menu_hardware_transtape_version_cond,
            "Rom file","Archivo Rom","Arxiu Rom");
        menu_add_item_menu_prefijo_format(array_menu_common,"[%s] ",(transtape_rom_filename[0]==0 ? "Default" : string_rom_file_shown));

        menu_add_item_menu_separator(array_menu_common);

        menu_add_item_menu_en_es_ca(array_menu_common,MENU_OPCION_NORMAL,menu_hardware_transtape_switch_saveload,NULL,
            "Switch A10","Conmutador A10","Conmutador A10");
        menu_add_item_menu_prefijo_format(array_menu_common,"[%s] ",(transtape_switch_a10.v ? "1=Save" : "0=Load"));


        char buffer_funcion_a11_zero[30];
        char buffer_funcion_a11_one[30];
        /*
        //0=menu, 1=no menu en transtape 3
        //0=microdrive, 1=cinta en transtape 2
        */
        if (transtape_version==2) {
            strcpy(buffer_funcion_a11_zero,"0=Microdrive");
            strcpy(buffer_funcion_a11_one,"1=Tape");
        }
        else {
            strcpy(buffer_funcion_a11_zero,"0=Menu");
            strcpy(buffer_funcion_a11_one,"1=Tape");
        }

        menu_add_item_menu_en_es_ca(array_menu_common,MENU_OPCION_NORMAL,menu_hardware_transtape_switch_menu,NULL,
            "Switch A11","Conmutador A11","Conmutador A11");
        menu_add_item_menu_prefijo_format(array_menu_common,"[%s] ",(transtape_switch_a11.v ? buffer_funcion_a11_one : buffer_funcion_a11_zero));

        if (transtape_enabled.v) {
            menu_add_item_menu_en_es_ca(array_menu_common,MENU_OPCION_NORMAL,menu_hardware_transtape_reset_button,NULL,
                "Simulate reset press","Simular pulsación reset","Simular polsació reset");
        }


        menu_add_item_menu_separator(array_menu_common);

        menu_add_ESC_item(array_menu_common);

        retorno_menu=menu_dibuja_menu_no_title_lang(&transtape_opcion_seleccionada,&item_seleccionado,array_menu_common,"Transtape emulation");


        if ((item_seleccionado.tipo_opcion&MENU_OPCION_ESC)==0 && retorno_menu>=0) {
            //llamamos por valor de funcion
                if (item_seleccionado.menu_funcion!=NULL) {
                //printf ("actuamos por funcion\n");
                item_seleccionado.menu_funcion(item_seleccionado.valor_opcion);

            }
        }

    } while ( (item_seleccionado.tipo_opcion&MENU_OPCION_ESC)==0 && retorno_menu!=MENU_RETORNO_ESC && !salir_todos_menus);




}

void menu_hardware_specmate_enable(MENU_ITEM_PARAMETERS)
{
    if (specmate_enabled.v) {
        specmate_disable();
    }
    else {
        specmate_enable();
    }
}



void menu_specmate(MENU_ITEM_PARAMETERS)
{
    menu_item *array_menu_common;
    menu_item item_seleccionado;
    int retorno_menu;
    do {



        menu_add_item_menu_inicial_format(&array_menu_common,MENU_OPCION_NORMAL,menu_hardware_specmate_enable,
                NULL,"[%c] ~~Spec-Mate Enabled", (specmate_enabled.v ? 'X' : ' '));
        menu_add_item_menu_shortcut(array_menu_common,'s');
        menu_add_item_menu_tooltip(array_menu_common,"Enable Spec-Mate");

        menu_add_item_menu_ayuda(array_menu_common,"After enabling the interface and press NMI, you will be directed to BLACK border mode, "
            "look at the following modes:\n"
            "\n"
            "BLACK BORDER MODE\n"
            "\n"
            "t = save to tape\n"
            "m = save to microdrive\n"
            "d = save to disc drive (NB this applies to Beta Interface Only)\n"
            "w = save to wafadrive\n"
            "f =  save 'FAST' to tape (i.e. twice normal tape speed)\n"
            "0 = remove attirbutes\n"
            "\n"
            "RED BORDER MODE\n"
            "\n"
            "n = normal save with one screen\n"
            "s = screenless save\n"
            "a = save part A of program (with double screen)\n"
            "b = save part B of program (with double screen)\n"
            "\n"
            "BLUE BORDER MODE\n"
            "\n"
            "1 = use top part of screen for *SPEC-MATE* data\n"
            "2 = use middle part of screen for *SPEC-MATE* data\n"
            "3 = use bottom part of screen for *SPEC-MATE* data\n"
            "\n"
            "MAGENTA BORDER MODE\n"
            "\n"
            "Type the name of the program. Press 'ENTER' to save\n"
        );

        menu_add_item_menu_separator(array_menu_common);

        menu_add_ESC_item(array_menu_common);

        retorno_menu=menu_dibuja_menu_no_title_lang(&specmate_opcion_seleccionada,&item_seleccionado,array_menu_common,"Spec-mate emulation");


        if ((item_seleccionado.tipo_opcion&MENU_OPCION_ESC)==0 && retorno_menu>=0) {
            //llamamos por valor de funcion
                if (item_seleccionado.menu_funcion!=NULL) {
                //printf ("actuamos por funcion\n");
                item_seleccionado.menu_funcion(item_seleccionado.valor_opcion);

            }
        }

    } while ( (item_seleccionado.tipo_opcion&MENU_OPCION_ESC)==0 && retorno_menu!=MENU_RETORNO_ESC && !salir_todos_menus);




}

void menu_hardware_interface007_enable(MENU_ITEM_PARAMETERS)
{
    if (interface007_enabled.v) {
        interface007_disable();
    }
    else {
        interface007_enable();
    }
}



void menu_interface007(MENU_ITEM_PARAMETERS)
{
    menu_item *array_menu_common;
    menu_item item_seleccionado;
    int retorno_menu;
    do {



        menu_add_item_menu_inicial_format(&array_menu_common,MENU_OPCION_NORMAL,menu_hardware_interface007_enable,
                NULL,"[%c] ~~Interface007 Enabled", (interface007_enabled.v ? 'X' : ' '));
        menu_add_item_menu_shortcut(array_menu_common,'i');
        menu_add_item_menu_tooltip(array_menu_common,"Enable Interface007");

        menu_add_item_menu_ayuda(array_menu_common,"After enabling the interface and press NMI, press: \n"
            "1: to save at normal speed\n"
            "2, 3, 4 or 5: to save at 2x, 3x, 4x or 5x speed\n"
        );

        menu_add_item_menu_separator(array_menu_common);

        menu_add_ESC_item(array_menu_common);

        retorno_menu=menu_dibuja_menu_no_title_lang(&interface007_opcion_seleccionada,&item_seleccionado,array_menu_common,"Interface007 emulation");


        if ((item_seleccionado.tipo_opcion&MENU_OPCION_ESC)==0 && retorno_menu>=0) {
            //llamamos por valor de funcion
                if (item_seleccionado.menu_funcion!=NULL) {
                //printf ("actuamos por funcion\n");
                item_seleccionado.menu_funcion(item_seleccionado.valor_opcion);

            }
        }

    } while ( (item_seleccionado.tipo_opcion&MENU_OPCION_ESC)==0 && retorno_menu!=MENU_RETORNO_ESC && !salir_todos_menus);




}

void menu_hardware_dinamid3_enable(MENU_ITEM_PARAMETERS)
{
    if (dinamid3_enabled.v) {
        dinamid3_disable();
    }
    else {
        dinamid3_enable();
    }
}



void menu_dinamid3(MENU_ITEM_PARAMETERS)
{
    menu_item *array_menu_common;
    menu_item item_seleccionado;
    int retorno_menu;
    do {



        menu_add_item_menu_inicial_format(&array_menu_common,MENU_OPCION_NORMAL,menu_hardware_dinamid3_enable,
                NULL,"[%c] ~~Dinamid3 Enabled", (dinamid3_enabled.v ? 'X' : ' '));
        menu_add_item_menu_shortcut(array_menu_common,'d');
        menu_add_item_menu_tooltip(array_menu_common,"Enable Dinamid3");

        menu_add_item_menu_ayuda(array_menu_common,"After enabling the interface and press NMI, press: \n"
            "1: save (seems it doesn't read any other key). Then:\n"
            "s: save snapshot to tape\n"
            "d: save snapshot to tape with a copy program that copies it to microdrive"
        );

        menu_add_item_menu_separator(array_menu_common);

        menu_add_ESC_item(array_menu_common);

        retorno_menu=menu_dibuja_menu_no_title_lang(&dinamid3_opcion_seleccionada,&item_seleccionado,array_menu_common,"Dinamid3 emulation");


        if ((item_seleccionado.tipo_opcion&MENU_OPCION_ESC)==0 && retorno_menu>=0) {
            //llamamos por valor de funcion
                if (item_seleccionado.menu_funcion!=NULL) {
                //printf ("actuamos por funcion\n");
                item_seleccionado.menu_funcion(item_seleccionado.valor_opcion);

            }
        }

    } while ( (item_seleccionado.tipo_opcion&MENU_OPCION_ESC)==0 && retorno_menu!=MENU_RETORNO_ESC && !salir_todos_menus);




}


void menu_run_mantransfer(MENU_ITEM_PARAMETERS)
{
	//Cargar mantransfer.bin
	char *mantransfefilename="mantransfev3.bin";
    FILE *ptr_mantransfebin;
    int leidos;

    open_sharedfile(mantransfefilename,&ptr_mantransfebin);
    if (!ptr_mantransfebin)
    {
            debug_printf(VERBOSE_ERR,"Unable to open mantransfer binary file %s",mantransfefilename);
            return;
    }

	#define MAX_MANTRANSFE_BIN 1024
	z80_byte buffer[MAX_MANTRANSFE_BIN];

	leidos=fread(buffer,1,MAX_MANTRANSFE_BIN,ptr_mantransfebin);

	//pasarlo a memoria
	int i;

	for (i=0;i<leidos;i++) {
		poke_byte_no_time(16384+i,buffer[i]);
	}

	fclose(ptr_mantransfebin);

	//Establecer modo im1 o im2
	//Al inicio hay
	//regsp defw 0
	//im 1  (0xed,0x56)

	//im2 seria 0xed, 0x5e

	int offset_im_opcode=16384+2+1;
	z80_byte opcode;

	if (im_mode==1 || im_mode==0) opcode=0x56;
	//im2
	else opcode=0x5e;

	poke_byte_no_time(offset_im_opcode,opcode);

	debug_printf (VERBOSE_INFO,"Running mantransfer saving routine");

	//y saltar a la rutina de grabacion de mantransfe
	//Si se cambia la rutina, hay que cambiar este salto tambien
    push_valor(reg_pc,PUSH_VALUE_TYPE_CALL);
    reg_pc=16384+0x32;

    //Y salimos de todos los menus
    salir_todos_menus=1;

}



void menu_mantransfer(MENU_ITEM_PARAMETERS)
{
    menu_item *array_menu_common;
    menu_item item_seleccionado;
    int retorno_menu;
    do {



        menu_add_item_menu_inicial_format(&array_menu_common,MENU_OPCION_NORMAL,menu_run_mantransfer,
                NULL,"Run ~~Mantransfer");
        menu_add_item_menu_spanish_catalan(array_menu_common,"Ejecutar ~~Mantransfer","Executar ~~Mantransfer");
        menu_add_item_menu_shortcut(array_menu_common,'m');

		menu_add_item_menu_tooltip(array_menu_common,"Run mantransfer, which dumps ram memory contents (snapshot) to Spectrum Tape\n"
					"Only Spectrum 48k/16k models supported");
		menu_add_item_menu_ayuda(array_menu_common,"Mantransfer is a software-based copy interface. It was coded by me, in the 90's, "
                    "as an alternative to a hardware-based copy interface (transtape, multiface, etc).\n"
                    "It was kept resident on memory by using IM2 interrupts, and triggered by a key combination\n"
                    "The difference between this option and the Save snapshot option is that "
					"this option runs a Spectrum machine program (mantransfev3.bin) which dumps the ram contents to tape, "
					"so you can use a .tap file to save it or even a real tape connected to line out of your soundcard.\n"
					"It uses a small amount of RAM on memory display and some bytes on the stack, so it is not a perfect "
					"routine and sometimes may fail.\n"
					"The source code can be found on mantransfev3.tap\n"
					"Note: Although mantransfe is a Spectrum program and it could run on a real spectrum or another emulator, "
					"the saving routine needs that ZEsarUX emulator tells which im mode the cpu is (IM1 or IM2), "
					"so, a saved program can be run on a real spectrum or another emulator, "
					"but the saving routine sees im1 by default, so, saving from a real spectrum or another emulator "
					"instead ZEsarUX will only work if the cpu is in IM1 mode (and not IM2)");

        menu_add_item_menu_separator(array_menu_common);

        menu_add_ESC_item(array_menu_common);

        retorno_menu=menu_dibuja_menu_no_title_lang(&mantransfe_opcion_seleccionada,&item_seleccionado,array_menu_common,"Mantransfer");


        if ((item_seleccionado.tipo_opcion&MENU_OPCION_ESC)==0 && retorno_menu>=0) {
            //llamamos por valor de funcion
                if (item_seleccionado.menu_funcion!=NULL) {
                //printf ("actuamos por funcion\n");
                item_seleccionado.menu_funcion(item_seleccionado.valor_opcion);

            }
        }

    } while ( (item_seleccionado.tipo_opcion&MENU_OPCION_ESC)==0 && retorno_menu!=MENU_RETORNO_ESC && !salir_todos_menus);




}



void menu_hardware_phoenix_enable(MENU_ITEM_PARAMETERS)
{
    if (phoenix_enabled.v) {
        phoenix_disable();
    }
    else {
        phoenix_enable();
    }
}



void menu_phoenix(MENU_ITEM_PARAMETERS)
{
    menu_item *array_menu_common;
    menu_item item_seleccionado;
    int retorno_menu;
    do {



        menu_add_item_menu_inicial_format(&array_menu_common,MENU_OPCION_NORMAL,menu_hardware_phoenix_enable,
                NULL,"[%c] ~~Phoenix Enabled", (phoenix_enabled.v ? 'X' : ' '));
        menu_add_item_menu_shortcut(array_menu_common,'p');
        menu_add_item_menu_tooltip(array_menu_common,"Enable Phoenix");

        menu_add_item_menu_ayuda(array_menu_common,"After enabling the interface and press NMI, you must press: \n"
            "1: save snapshot for microdrive or disk\n"
            "2: save snapshot to tape at normal speed (1500 bauds)\n"
            "3: save snapshot to tape at turbo speed (2000 bauds)\n"
            "4: save snapshot to tape at turbo speed (2500 bauds)\n"
            "5: return to game, using the snapshot restore routine in order to check the snapshot is valid\n"
        );


        menu_add_item_menu_separator(array_menu_common);

        menu_add_ESC_item(array_menu_common);

        retorno_menu=menu_dibuja_menu_no_title_lang(&phoenix_opcion_seleccionada,&item_seleccionado,array_menu_common,"Phoenix emulation");


        if ((item_seleccionado.tipo_opcion&MENU_OPCION_ESC)==0 && retorno_menu>=0) {
            //llamamos por valor de funcion
                if (item_seleccionado.menu_funcion!=NULL) {
                //printf ("actuamos por funcion\n");
                item_seleccionado.menu_funcion(item_seleccionado.valor_opcion);

            }
        }

    } while ( (item_seleccionado.tipo_opcion&MENU_OPCION_ESC)==0 && retorno_menu!=MENU_RETORNO_ESC && !salir_todos_menus);




}

void menu_hardware_defcon_enable(MENU_ITEM_PARAMETERS)
{
    if (defcon_enabled.v) {
        defcon_disable();
    }
    else {
        defcon_enable();
    }
}



void menu_defcon(MENU_ITEM_PARAMETERS)
{
    menu_item *array_menu_common;
    menu_item item_seleccionado;
    int retorno_menu;
    do {



        menu_add_item_menu_inicial_format(&array_menu_common,MENU_OPCION_NORMAL,menu_hardware_defcon_enable,
            NULL,"[%c] ~~Defcon Enabled", (defcon_enabled.v ? 'X' : ' '));
        menu_add_item_menu_shortcut(array_menu_common,'d');
        menu_add_item_menu_tooltip(array_menu_common,"Enable Defcon");
        menu_add_item_menu_ayuda(array_menu_common,"After enabling the interface and press NMI, you must press: \n"
            "1: to save screen and loader program\n"
            "2: to save snapshot data\n"
            "To load the snapshot, just:\n"
            "LOAD \"\" CODE: LOAD \"\" CODE: RANDOMIZE USR 16388\n"
        );




        menu_add_item_menu_separator(array_menu_common);

        menu_add_ESC_item(array_menu_common);

        retorno_menu=menu_dibuja_menu_no_title_lang(&defcon_opcion_seleccionada,&item_seleccionado,array_menu_common,"Defcon emulation");


        if ((item_seleccionado.tipo_opcion&MENU_OPCION_ESC)==0 && retorno_menu>=0) {
            //llamamos por valor de funcion
                if (item_seleccionado.menu_funcion!=NULL) {
                //printf ("actuamos por funcion\n");
                item_seleccionado.menu_funcion(item_seleccionado.valor_opcion);

            }
        }

    } while ( (item_seleccionado.tipo_opcion&MENU_OPCION_ESC)==0 && retorno_menu!=MENU_RETORNO_ESC && !salir_todos_menus);




}

void menu_hardware_ramjet_enable(MENU_ITEM_PARAMETERS)
{
    if (ramjet_enabled.v) {
        ramjet_disable();
    }
    else {
        ramjet_enable();
    }
}

int menu_hardware_ramjet_version_cond(void)
{
    if (ramjet_enabled.v) return 0;
    else return 1;
}

void menu_hardware_ramjet_version(MENU_ITEM_PARAMETERS)
{
    if (ramjet_version==2) ramjet_version=3;
    else ramjet_version=2;
}

void menu_ramjet(MENU_ITEM_PARAMETERS)
{
    menu_item *array_menu_common;
    menu_item item_seleccionado;
    int retorno_menu;
    do {


        menu_add_item_menu_inicial_format(&array_menu_common,MENU_OPCION_NORMAL,menu_hardware_ramjet_enable,
            NULL,"[%c] ~~Ramjet Enabled", (ramjet_enabled.v ? 'X' : ' '));
        menu_add_item_menu_shortcut(array_menu_common,'r');
        menu_add_item_menu_tooltip(array_menu_common,"Enable Ramjet");
        menu_add_item_menu_ayuda(array_menu_common,"After enabling the interface and press NMI, you may: \n"
            "- Reset cpu: Ramjet boot will appear, with the copier program\n"
            "- Press NMI: the following keys are used:\n"
            "t: save 48k snapshot to disk\n"
            "l: save 128k snapshot to disk\n"
            "s: save 48k snapshot to tape\n"
            "u: save screen to tape\n"
            "c: save 128k snapshot to tape\n"
            "p: poke program\n"
            "r: return to program\n"
            "\n"
            "When saving snapshots, you will be asked for a name, just write it and press enter\n"
        );

        menu_add_item_menu_format(array_menu_common,MENU_OPCION_NORMAL,menu_hardware_ramjet_version,
            menu_hardware_ramjet_version_cond,"[%d] Ramjet ~~Version", ramjet_version);
        menu_add_item_menu_shortcut(array_menu_common,'v');
        menu_add_item_menu_tooltip(array_menu_common,"It seems version 2 is compatible with 48k/128k and version 3 is compatible with +2A/+3");
        menu_add_item_menu_ayuda(array_menu_common,"It seems version 2 is compatible with 48k/128k and version 3 is compatible with +2A/+3");

        menu_add_item_menu_separator(array_menu_common);

        menu_add_ESC_item(array_menu_common);

        retorno_menu=menu_dibuja_menu_no_title_lang(&ramjet_opcion_seleccionada,&item_seleccionado,array_menu_common,"Ramjet emulation");


        if ((item_seleccionado.tipo_opcion&MENU_OPCION_ESC)==0 && retorno_menu>=0) {
            //llamamos por valor de funcion
                if (item_seleccionado.menu_funcion!=NULL) {
                //printf ("actuamos por funcion\n");
                item_seleccionado.menu_funcion(item_seleccionado.valor_opcion);

            }
        }

    } while ( (item_seleccionado.tipo_opcion&MENU_OPCION_ESC)==0 && retorno_menu!=MENU_RETORNO_ESC && !salir_todos_menus);




}



void menu_hardware_mhpokeador_enable(MENU_ITEM_PARAMETERS)
{
    if (mhpokeador_enabled.v) {
        mhpokeador_disable();
    }
    else {
        mhpokeador_enable();
    }
}


int menu_hardware_mhpokeador_enabled_cond(void)
{
    if (mhpokeador_enabled.v) return 0;
    else return 1;
}


void menu_hardware_mhpokeador_romfile(MENU_ITEM_PARAMETERS)
{

    char *filtros[2];

    filtros[0]="rom";
    filtros[1]=0;


    if (menu_filesel("Select ROM File",filtros,mhpokeador_rom_filename)==1) {
        if (!si_existe_archivo(mhpokeador_rom_filename)) {
            menu_error_message("File does not exist");
            mhpokeador_rom_filename[0]=0;
            return;
        }

        else {
            //Comprobar aqui tambien el tamanyo
            long long int size=get_file_size(mhpokeador_rom_filename);
            if (size!=MHPOKEADOR_ROM_SIZE) {
                menu_error_message("ROM file is not expected size");
                mhpokeador_rom_filename[0]=0;
                return;
            }
        }

    }
    //Sale con ESC
    else {
        //Quitar nombre
        mhpokeador_rom_filename[0]=0;
    }

}

void menu_hardware_mhpokeador_tipo(MENU_ITEM_PARAMETERS)
{
    mhpokeador_tipo_rom_cargar++;
    if (mhpokeador_tipo_rom_cargar>MHPOKEADOR_TIPO_ROM_SALVAPAN) mhpokeador_tipo_rom_cargar=MHPOKEADOR_TIPO_ROM_POKEADOR;

}

void menu_mhpokeador(MENU_ITEM_PARAMETERS)
{
    menu_item *array_menu_common;
    menu_item item_seleccionado;
    int retorno_menu;
    do {



        menu_add_item_menu_inicial_format(&array_menu_common,MENU_OPCION_NORMAL,menu_hardware_mhpokeador_enable,
                NULL,"[%c] ~~Pokeador Enabled", (mhpokeador_enabled.v ? 'X' : ' '));
        menu_add_item_menu_shortcut(array_menu_common,'p');
        menu_add_item_menu_tooltip(array_menu_common,"Enable Microhobby Pokeador Automático");
        menu_add_item_menu_ayuda(array_menu_common,"Enable Microhobby Pokeador Automático");



        char string_rom_file_shown[20];
        menu_tape_settings_trunc_name(mhpokeador_rom_filename,string_rom_file_shown,20);

        menu_add_item_menu_en_es_ca(array_menu_common,MENU_OPCION_NORMAL,menu_hardware_mhpokeador_romfile,menu_hardware_mhpokeador_enabled_cond,
            "Rom file","Archivo Rom","Arxiu Rom");
        menu_add_item_menu_prefijo_format(array_menu_common,"[%s] ",(mhpokeador_rom_filename[0]==0 ? "Default" : string_rom_file_shown));



        char buffer_tipo[30];
        if (mhpokeador_tipo_rom_cargar==MHPOKEADOR_TIPO_ROM_TRANSFER) {
            strcpy(buffer_tipo,"Transfer");
        }

        else if (mhpokeador_tipo_rom_cargar==MHPOKEADOR_TIPO_ROM_SALVAPAN) {
            strcpy(buffer_tipo,"Save Screen");
        }

        else strcpy(buffer_tipo,"Pokeador");

        menu_add_item_menu_en_es_ca(array_menu_common,MENU_OPCION_NORMAL,menu_hardware_mhpokeador_tipo,menu_hardware_mhpokeador_enabled_cond,
            "Type","Tipo","Tipus");
        menu_add_item_menu_prefijo_format(array_menu_common,"[%s] ",buffer_tipo);

        menu_add_item_menu_separator(array_menu_common);


        menu_add_ESC_item(array_menu_common);

        retorno_menu=menu_dibuja_menu_no_title_lang(&mhpokeador_opcion_seleccionada,&item_seleccionado,array_menu_common,"Microhobby Pokeador emulation");


        if ((item_seleccionado.tipo_opcion&MENU_OPCION_ESC)==0 && retorno_menu>=0) {
            //llamamos por valor de funcion
                if (item_seleccionado.menu_funcion!=NULL) {
                //printf ("actuamos por funcion\n");
                item_seleccionado.menu_funcion(item_seleccionado.valor_opcion);

            }
        }

    } while ( (item_seleccionado.tipo_opcion&MENU_OPCION_ESC)==0 && retorno_menu!=MENU_RETORNO_ESC && !salir_todos_menus);




}


void menu_multiface_rom_file(MENU_ITEM_PARAMETERS)
{
	multiface_disable();

        char *filtros[2];

        filtros[0]="rom";
        filtros[1]=0;


        if (menu_filesel("Select multiface File",filtros,multiface_rom_file_name)==1) {
                if (!si_existe_archivo(multiface_rom_file_name)) {
                        menu_error_message("File does not exist");
                        multiface_rom_file_name[0]=0;
                        return;



                }

                else {
                        //Comprobar aqui tambien el tamanyo
                        long long int size=get_file_size(multiface_rom_file_name);
                        if (size!=8192) {
                                menu_error_message("ROM file must be 8 KB length");
                                multiface_rom_file_name[0]=0;
                                return;
                        }
                }


        }
        //Sale con ESC
        else {
                //Quitar nombre
                multiface_rom_file_name[0]=0;


        }

}


void menu_hardware_multiface_enable(MENU_ITEM_PARAMETERS)
{
	if (multiface_enabled.v) multiface_disable();
	else multiface_enable();
}

void menu_hardware_multiface_type(MENU_ITEM_PARAMETERS)
{
  multiface_type++;
  if (multiface_type==MULTIFACE_TOTAL_TYPES)  multiface_type=0;
}

int menu_hardware_multiface_type_cond(void)
{
	//En tbblue no se puede cambiar el tipo
	if (MACHINE_IS_TBBLUE) return 0;

	return !multiface_enabled.v;
}

void menu_multiface(MENU_ITEM_PARAMETERS)
{
        menu_item *array_menu_multiface;
        menu_item item_seleccionado;
        int retorno_menu;
        do {

                char string_multiface_file_shown[13];

				//Como no sabemos cual sera el item inicial, metemos este sin asignar, que se sobreescribe en el siguiente menu_add_item_menu
                menu_add_item_menu_inicial(&array_menu_multiface,"",MENU_OPCION_UNASSIGNED,NULL,NULL);
					//En tbblue, la rom no es seleccionable, la carga el mismo en la sdram

					if (!MACHINE_IS_TBBLUE) {
                        menu_tape_settings_trunc_name(multiface_rom_file_name,string_multiface_file_shown,13);
                        menu_add_item_menu_format(array_menu_multiface,MENU_OPCION_NORMAL,menu_multiface_rom_file,NULL,"~~ROM File [%s]",string_multiface_file_shown);
                        menu_add_item_menu_shortcut(array_menu_multiface,'r');
                        menu_add_item_menu_tooltip(array_menu_multiface,"ROM Emulation file");
                        menu_add_item_menu_ayuda(array_menu_multiface,"ROM Emulation file");
					}


                        menu_add_item_menu_format(array_menu_multiface,MENU_OPCION_NORMAL,menu_hardware_multiface_type,menu_hardware_multiface_type_cond,"~~Type [%s]",multiface_types_string[multiface_type]);
                  menu_add_item_menu_shortcut(array_menu_multiface,'t');
                  menu_add_item_menu_tooltip(array_menu_multiface,"Multiface type. You must first disable it if you want to change type");
                  menu_add_item_menu_ayuda(array_menu_multiface,"Multiface type. You must first disable it if you want to change type");


                        			menu_add_item_menu_format(array_menu_multiface,MENU_OPCION_NORMAL,menu_hardware_multiface_enable,NULL,"[%c] ~~Multiface Enabled", (multiface_enabled.v ? 'X' : ' '));
                        menu_add_item_menu_shortcut(array_menu_multiface,'m');
                        menu_add_item_menu_tooltip(array_menu_multiface,"Enable multiface");
                        menu_add_item_menu_ayuda(array_menu_multiface,"Enable multiface");





                                menu_add_item_menu(array_menu_multiface,"",MENU_OPCION_SEPARADOR,NULL,NULL);

                menu_add_ESC_item(array_menu_multiface);

                retorno_menu=menu_dibuja_menu_no_title_lang(&multiface_opcion_seleccionada,&item_seleccionado,array_menu_multiface,"Multiface emulation");


                if ((item_seleccionado.tipo_opcion&MENU_OPCION_ESC)==0 && retorno_menu>=0) {
                        //llamamos por valor de funcion
                        if (item_seleccionado.menu_funcion!=NULL) {
                                //printf ("actuamos por funcion\n");
                                item_seleccionado.menu_funcion(item_seleccionado.valor_opcion);

                        }
                }

        } while ( (item_seleccionado.tipo_opcion&MENU_OPCION_ESC)==0 && retorno_menu!=MENU_RETORNO_ESC && !salir_todos_menus);




}



void menu_storage_copy_devices(MENU_ITEM_PARAMETERS)
{
    menu_item *array_menu_common;
    menu_item item_seleccionado;
    int retorno_menu;


    do {

        menu_add_item_menu_inicial_format(&array_menu_common,MENU_OPCION_NORMAL,menu_defcon,NULL,"~~Defcon");
        menu_add_item_menu_shortcut(array_menu_common,'d');
        menu_add_item_menu_tiene_submenu(array_menu_common);

        menu_add_item_menu_format(array_menu_common,MENU_OPCION_NORMAL,menu_dinamid3,NULL,"D~~inamid3");
        menu_add_item_menu_shortcut(array_menu_common,'i');
        menu_add_item_menu_tiene_submenu(array_menu_common);

        menu_add_item_menu_format(array_menu_common,MENU_OPCION_NORMAL,menu_hilow_barbanegra,NULL,"HiLow ~~Barbanegra");
        menu_add_item_menu_shortcut(array_menu_common,'b');
        menu_add_item_menu_tiene_submenu(array_menu_common);

        menu_add_item_menu_format(array_menu_common,MENU_OPCION_NORMAL,menu_interface007,NULL,"I~~nterface007");
        menu_add_item_menu_shortcut(array_menu_common,'n');
        menu_add_item_menu_tiene_submenu(array_menu_common);

        if (MACHINE_IS_SPECTRUM_16_48) {
            menu_add_item_menu_format(array_menu_common,MENU_OPCION_NORMAL,menu_mantransfer,NULL,"M~~antransfer");
            menu_add_item_menu_shortcut(array_menu_common,'a');
            menu_add_item_menu_tiene_submenu(array_menu_common);
        }

        menu_add_item_menu_format(array_menu_common,MENU_OPCION_NORMAL,menu_mhpokeador,NULL,"Microhobby ~~Pokeador Automático");
        menu_add_item_menu_shortcut(array_menu_common,'p');
        menu_add_item_menu_tiene_submenu(array_menu_common);

        menu_add_item_menu_format(array_menu_common,MENU_OPCION_NORMAL,menu_multiface,NULL,"~~Multiface");
        menu_add_item_menu_shortcut(array_menu_common,'m');
        menu_add_item_menu_tiene_submenu(array_menu_common);

        menu_add_item_menu_format(array_menu_common,MENU_OPCION_NORMAL,menu_phoenix,NULL,"P~~hoenix");
        menu_add_item_menu_shortcut(array_menu_common,'h');
        menu_add_item_menu_tiene_submenu(array_menu_common);

        menu_add_item_menu_format(array_menu_common,MENU_OPCION_NORMAL,menu_ramjet,NULL,"~~Ramjet");
        menu_add_item_menu_shortcut(array_menu_common,'r');
        menu_add_item_menu_tiene_submenu(array_menu_common);

        menu_add_item_menu_format(array_menu_common,MENU_OPCION_NORMAL,menu_specmate,NULL,"~~Spec-Mate");
        menu_add_item_menu_shortcut(array_menu_common,'s');
        menu_add_item_menu_tiene_submenu(array_menu_common);

        menu_add_item_menu_format(array_menu_common,MENU_OPCION_NORMAL,menu_transtape,NULL,"~~Transtape");
        menu_add_item_menu_shortcut(array_menu_common,'t');
        menu_add_item_menu_tiene_submenu(array_menu_common);



        menu_add_item_menu_separator(array_menu_common);

        menu_add_ESC_item(array_menu_common);

        retorno_menu=menu_dibuja_menu_no_title_lang(&storage_copy_devices_opcion_seleccionada,&item_seleccionado,array_menu_common,"Copy Interfaces");



        if ((item_seleccionado.tipo_opcion&MENU_OPCION_ESC)==0 && retorno_menu>=0) {
            //llamamos por valor de funcion
            if (item_seleccionado.menu_funcion!=NULL) {
                //printf ("actuamos por funcion\n");
                item_seleccionado.menu_funcion(item_seleccionado.valor_opcion);

            }
        }

    } while ( (item_seleccionado.tipo_opcion&MENU_OPCION_ESC)==0 && retorno_menu!=MENU_RETORNO_ESC && !salir_todos_menus);

}

void menu_cpc_additional_roms_enable(MENU_ITEM_PARAMETERS)
{
	cpc_additional_roms[valor_opcion].enabled ^=1;
}

void menu_cpc_additional_roms_bank(MENU_ITEM_PARAMETERS)
{
	int bank=cpc_additional_roms[valor_opcion].bank_number;

	menu_ventana_scanf_numero_enhanced("Bank number",&bank,4,+1,0,255,0);

	cpc_additional_roms[valor_opcion].bank_number=bank;
}

void menu_cpc_additional_roms_load(MENU_ITEM_PARAMETERS)
{
	int offset_rom=16384*valor_opcion;

    //char romfile[PATH_MAX];

    char *filtros[2];

    filtros[0]="rom";
    filtros[1]=0;


    if (menu_filesel("Select ROM File",filtros,cpc_additional_rom_file_name[valor_opcion])==1) {
		util_load_file_bytes(&cpc_additional_rom_pointer[offset_rom],cpc_additional_rom_file_name[valor_opcion],16384);

		//habilitamos esa rom
		cpc_additional_roms[valor_opcion].enabled=1;
            //Y salimos de todos los menus
            salir_todos_menus=1;

    }
}

void menu_cpc_additional_roms_info(MENU_ITEM_PARAMETERS)
{


	int offset_rom=16384*valor_opcion;


	//Command table pointer en bytes 4 y 5 (y apuntando a C000 en adelante)
	z80_int command_table_ptr=value_8_to_16(cpc_additional_rom_pointer[offset_rom+5],cpc_additional_rom_pointer[offset_rom+4]);

	command_table_ptr &=16383;

	int puntero_total=offset_rom+command_table_ptr;

	z80_byte *puntero=&cpc_additional_rom_pointer[puntero_total];

	//Mostrar los dos primeros comandos

    char *text_buffer=util_malloc_max_texto_generic_message("Can not allocate memory for showing stats");
    //Empezar con cadena vacia para poder concatenar
    text_buffer[0]=0;

	//margen suficiente para que quepa una linea ...
	char buf_linea[100];

	//Mostrar los 10 primeros comandos (de haberlos)
	//El primero es el nombre del cartucho

	int total_comandos=0;
	int salir=0;

	while (total_comandos<20 && !salir) {
		//Ultimo comando acaba con 0 en teoria, aunque protext por ejemplo tiene byte 16
		if (*puntero<32) salir=1;
		else {
			int i=0;
			//Hasta encontrar bit alzado
			while ( ((*puntero)&128)==0) {
				buf_linea[i]=*puntero;
				i++;
				puntero++;
			}

			buf_linea[i++]=(*puntero)&127;
			puntero++;
			buf_linea[i++]='\n';
			buf_linea[i]=0;
			//printf("[%s]\n",buf_linea);



			if (total_comandos==0) util_concat_string(text_buffer,"Name: ",MAX_TEXTO_GENERIC_MESSAGE);
			else util_concat_string(text_buffer,"|",MAX_TEXTO_GENERIC_MESSAGE);

			util_concat_string(text_buffer,buf_linea,MAX_TEXTO_GENERIC_MESSAGE);

			if (total_comandos==0) util_concat_string(text_buffer,"First 20 Commands:\n",MAX_TEXTO_GENERIC_MESSAGE);

			total_comandos++;

		}
	}


	menu_generic_message("ROM Information",text_buffer);



	free(text_buffer);
}

void menu_cpc_additional_roms(MENU_ITEM_PARAMETERS)
{
    menu_item *array_menu_common;
    menu_item item_seleccionado;
    int retorno_menu;


    do {

		menu_add_item_menu_inicial(&array_menu_common,"",MENU_OPCION_UNASSIGNED,NULL,NULL);

		int i;

		for (i=0;i<CPC_MAX_ADDITIONAL_ROMS;i++) {
			menu_add_item_menu_format(array_menu_common,MENU_OPCION_SEPARADOR,NULL,NULL,"Rom %d",i+1);


			menu_add_item_menu_format(array_menu_common,MENU_OPCION_NORMAL,menu_cpc_additional_roms_enable,NULL,
				"[%c] Enabled",(cpc_additional_roms[i].enabled ? 'X' : ' '));
			menu_add_item_menu_valor_opcion(array_menu_common,i);

			menu_add_item_menu_format(array_menu_common,MENU_OPCION_NORMAL,menu_cpc_additional_roms_bank,NULL,
				"    Bank [%d]",cpc_additional_roms[i].bank_number);
			menu_add_item_menu_valor_opcion(array_menu_common,i);


			char string_rom_file_shown[13];


			menu_tape_settings_trunc_name(cpc_additional_rom_file_name[i],string_rom_file_shown,13);

			menu_add_item_menu_format(array_menu_common,MENU_OPCION_NORMAL,menu_cpc_additional_roms_load,NULL,
				"    ROM File [%s]",string_rom_file_shown);
			menu_add_item_menu_valor_opcion(array_menu_common,i);
			menu_add_item_menu_add_flags(array_menu_common,MENU_ITEM_FLAG_GENERA_VENTANA | MENU_ITEM_FLAG_SE_CERRARA);

			if (cpc_additional_roms[i].enabled)	{
				menu_add_item_menu_format(array_menu_common,MENU_OPCION_NORMAL,menu_cpc_additional_roms_info,NULL,
					"    Info");
				menu_add_item_menu_valor_opcion(array_menu_common,i);
				menu_add_item_menu_add_flags(array_menu_common,MENU_ITEM_FLAG_GENERA_VENTANA | MENU_ITEM_FLAG_SE_CERRARA);
			}

			menu_add_item_menu_separator(array_menu_common);
		}



        //menu_add_item_menu_separator(array_menu_common);

        menu_add_ESC_item(array_menu_common);


        //Nota: si no se agrega el nombre del path del indice, se generará uno automáticamente
        menu_add_item_menu_index_full_path(array_menu_common,
            "Main Menu-> Storage-> Additional ROMS","Menú Principal-> Almacenamiento-> ROMS adicionales",
                "Menú Principal-> Emmagatzematge-> ROMS adicionals");

        retorno_menu=menu_dibuja_menu(&cpc_additional_roms_opcion_seleccionada,&item_seleccionado,array_menu_common,
            "Additional ROMS","ROMS adicionales","ROMS adicionals" );

        if ((item_seleccionado.tipo_opcion&MENU_OPCION_ESC)==0 && retorno_menu>=0) {
            //llamamos por valor de funcion
            if (item_seleccionado.menu_funcion!=NULL) {
                //printf ("actuamos por funcion\n");
                item_seleccionado.menu_funcion(item_seleccionado.valor_opcion);

            }
        }

    } while ( (item_seleccionado.tipo_opcion&MENU_OPCION_ESC)==0 && retorno_menu!=MENU_RETORNO_ESC && !salir_todos_menus);

}

void menu_lec_memory_enable(MENU_ITEM_PARAMETERS)
{
    if (lec_enabled.v) lec_disable();
    else lec_enable();
}

void menu_lec_memory_type(MENU_ITEM_PARAMETERS)
{
    lec_memory_type++;
    if (lec_memory_type==3) lec_memory_type=0;

    lec_set_memory_pages();
}


void menu_lec_memory(MENU_ITEM_PARAMETERS)
{
    menu_item *array_menu_common;
    menu_item item_seleccionado;
    int retorno_menu;


    do {

        menu_add_item_menu_inicial_format(&array_menu_common,MENU_OPCION_NORMAL,menu_lec_memory_enable,NULL,"~~Enable");
        menu_add_item_menu_shortcut(array_menu_common,'e');

        menu_add_item_menu_prefijo_format(array_menu_common,"[%c] ",(lec_enabled.v ? 'X' : ' ' ));

        char buffer_tipo[10];

        if (lec_memory_type==0) strcpy(buffer_tipo,"80");
        else if (lec_memory_type==1) strcpy(buffer_tipo,"272");
        else strcpy(buffer_tipo,"528");


        menu_add_item_menu_format(array_menu_common,MENU_OPCION_NORMAL,menu_lec_memory_type,NULL,"~~Type");
        menu_add_item_menu_sufijo_format(array_menu_common," [%s]",buffer_tipo);
        //menu_add_item_menu_shortcut(array_menu_common,'i');
        //menu_add_item_menu_tiene_submenu(array_menu_common);




        menu_add_item_menu_separator(array_menu_common);

        menu_add_ESC_item(array_menu_common);


        //Nota: si no se agrega el nombre del path del indice, se generará uno automáticamente
        menu_add_item_menu_index_full_path(array_menu_common,
            "Main Menu-> Storage-> LEC Memory","Menú Principal-> Almacenamiento-> LEC Memory","Menú Principal-> Emmagatzematge-> LEC Memory");

        retorno_menu=menu_dibuja_menu(&lec_memory_opcion_seleccionada,&item_seleccionado,array_menu_common,
            "LEC Memory Menu","Menú LEC Memory","Menú LEC Memory" );

        if ((item_seleccionado.tipo_opcion&MENU_OPCION_ESC)==0 && retorno_menu>=0) {
            //llamamos por valor de funcion
            if (item_seleccionado.menu_funcion!=NULL) {
                //printf ("actuamos por funcion\n");
                item_seleccionado.menu_funcion(item_seleccionado.valor_opcion);

            }
        }

    } while ( (item_seleccionado.tipo_opcion&MENU_OPCION_ESC)==0 && retorno_menu!=MENU_RETORNO_ESC && !salir_todos_menus);

}



//menu storage
void menu_storage(MENU_ITEM_PARAMETERS)
{
        menu_item *array_menu_storage;
        menu_item item_seleccionado;
        int retorno_menu;
        do {

                //char string_spi_flash_file_shown[13]; //,string_mmc_file_shown[13];

		 menu_add_item_menu_inicial(&array_menu_storage,"",MENU_OPCION_UNASSIGNED,NULL,NULL);


		if (MACHINE_IS_Z88) {
            int i;
            for (i=0;i<=3;i++) {
                menu_add_item_menu_format(array_menu_storage,MENU_OPCION_NORMAL,menu_z88_slots,NULL,"Z88 Memory Slot %d",i);
                //menu_add_item_menu_shortcut(array_menu_storage,'s');
                menu_add_item_menu_valor_opcion(array_menu_storage,i);

                menu_add_item_menu_tooltip(array_menu_storage,"Z88 Memory Slots");
                menu_add_item_menu_ayuda(array_menu_storage,"Selects Memory Slots to use on Z88");
                menu_add_item_menu_tiene_submenu(array_menu_storage);
            }

            //Si no hay pendiente una apertura o cierre

            if (!z88_pendiente_cerrar_tapa_timer) {

                menu_add_item_menu_separator(array_menu_storage);

                if (z88_flap_is_open() ) {
                    menu_add_item_menu_format(array_menu_storage,MENU_OPCION_NORMAL,menu_z88_slot_close_flap,NULL,"Close Z88 Flap");
                }
                else {
                    menu_add_item_menu_format(array_menu_storage,MENU_OPCION_NORMAL,menu_z88_slot_open_flap,NULL,"Open Z88 Flap");
                }

            }

		}

		else if (MACHINE_IS_QL) {
			menu_add_item_menu_format(array_menu_storage,MENU_OPCION_NORMAL,menu_ql_mdv_flp,NULL,"Microdrive & Floppy");
            menu_add_item_menu_tiene_submenu(array_menu_storage);



		}


		else if (!MACHINE_IS_CHLOE && !MACHINE_IS_COLECO && !MACHINE_IS_SG1000 && !MACHINE_IS_SMS && !MACHINE_IS_PCW) {
            menu_add_item_menu_format(array_menu_storage,MENU_OPCION_NORMAL,menu_storage_tape,menu_tape_settings_cond,"~~Tape");
            menu_add_item_menu_shortcut(array_menu_storage,'t');
            menu_add_item_menu_tooltip(array_menu_storage,"Select tape and options");
            menu_add_item_menu_ayuda(array_menu_storage,"Select tape for input (read) or output (write). \n"
                                                        "You use them as real tapes, with BASIC functions like LOAD and SAVE\n"
                                                        "Also select different options to change tape behaviour");
            menu_add_item_menu_tiene_submenu(array_menu_storage);
		}


		if (MACHINE_IS_SPECTRUM_P2A_P3 || MACHINE_IS_CPC_HAS_FLOPPY || MACHINE_IS_PCW) {
			menu_add_item_menu_format(array_menu_storage,MENU_OPCION_NORMAL,menu_plusthreedisk,NULL,"3\" CF2 F~~loppy");
			menu_add_item_menu_shortcut(array_menu_storage,'l');
			menu_add_item_menu_tooltip(array_menu_storage,"3\" Compact Floppy Disc emulation");
			menu_add_item_menu_ayuda(array_menu_storage,"3\" Compact Floppy Disc emulation, used on Spectrum +3, Amstrad CPC 6128,664, Amstrad PCW");
            menu_add_item_menu_tiene_submenu(array_menu_storage);
		}

		if (MACHINE_IS_CPC) {
			menu_add_item_menu_format(array_menu_storage,MENU_OPCION_NORMAL,menu_cpc_additional_roms,NULL,"Additional ROMS");
			menu_add_item_menu_tiene_submenu(array_menu_storage);
		}


		if (MACHINE_IS_TIMEX_TS_TC_2068) {
			menu_add_item_menu_format(array_menu_storage,MENU_OPCION_NORMAL,menu_timexcart,NULL,"Timex ~~Cartridge");
			menu_add_item_menu_shortcut(array_menu_storage,'c');
			menu_add_item_menu_tooltip(array_menu_storage,"Timex Cartridge Settings");
			menu_add_item_menu_ayuda(array_menu_storage,"Timex Cartridge Settings");
            menu_add_item_menu_tiene_submenu(array_menu_storage);
		}



		if (MACHINE_IS_MSX) {
			menu_add_item_menu_format(array_menu_storage,MENU_OPCION_NORMAL,menu_msxcart,NULL,"MSX ~~Cartridge");
			menu_add_item_menu_shortcut(array_menu_storage,'c');
			menu_add_item_menu_tooltip(array_menu_storage,"MSX Cartridge Settings");
			menu_add_item_menu_ayuda(array_menu_storage,"MSX Cartridge Settings");
            menu_add_item_menu_tiene_submenu(array_menu_storage);
		}

		if (MACHINE_IS_COLECO) {
			menu_add_item_menu_format(array_menu_storage,MENU_OPCION_NORMAL,menu_msxcart,NULL,"Coleco ~~Cartridge");
			menu_add_item_menu_shortcut(array_menu_storage,'c');
			menu_add_item_menu_tooltip(array_menu_storage,"Coleco Cartridge Settings");
			menu_add_item_menu_ayuda(array_menu_storage,"Coleco Cartridge Settings");
            menu_add_item_menu_tiene_submenu(array_menu_storage);
		}


		if (MACHINE_IS_SG1000) {
			menu_add_item_menu_format(array_menu_storage,MENU_OPCION_NORMAL,menu_msxcart,NULL,"SG-1000 ~~Cartridge");
			menu_add_item_menu_shortcut(array_menu_storage,'c');
			menu_add_item_menu_tooltip(array_menu_storage,"SG-1000 Cartridge Settings");
			menu_add_item_menu_ayuda(array_menu_storage,"SG-1000 Cartridge Settings");
            menu_add_item_menu_tiene_submenu(array_menu_storage);
		}

		if (MACHINE_IS_SMS) {
			menu_add_item_menu_format(array_menu_storage,MENU_OPCION_NORMAL,menu_msxcart,NULL,"SMS ~~Cartridge");
			menu_add_item_menu_shortcut(array_menu_storage,'c');
			menu_add_item_menu_tooltip(array_menu_storage,"SMS Cartridge Settings");
			menu_add_item_menu_ayuda(array_menu_storage,"SMS Cartridge Settings");
            menu_add_item_menu_tiene_submenu(array_menu_storage);
		}

		if (MACHINE_IS_SVI) {
			menu_add_item_menu_format(array_menu_storage,MENU_OPCION_NORMAL,menu_msxcart,NULL,"SVI ~~Cartridge");
			menu_add_item_menu_shortcut(array_menu_storage,'c');
			menu_add_item_menu_tooltip(array_menu_storage,"SVI Cartridge Settings");
			menu_add_item_menu_ayuda(array_menu_storage,"SVI Cartridge Settings");
            menu_add_item_menu_tiene_submenu(array_menu_storage);
		}


		if (MACHINE_IS_SPECTRUM) {
            menu_add_item_menu_format(array_menu_storage,MENU_OPCION_NORMAL,menu_betadisk,NULL,"~~Betadisk");
            menu_add_item_menu_shortcut(array_menu_storage,'b');
            menu_add_item_menu_tooltip(array_menu_storage,"Betadisk settings");
            menu_add_item_menu_ayuda(array_menu_storage,"Betadisk settings");
            menu_add_item_menu_tiene_submenu(array_menu_storage);
		}

		if (MACHINE_IS_SPECTRUM) {
			menu_add_item_menu_format(array_menu_storage,MENU_OPCION_NORMAL,menu_esxdos_traps,NULL,"~~ESXDOS Handler");
			menu_add_item_menu_shortcut(array_menu_storage,'e');
			menu_add_item_menu_tooltip(array_menu_storage,"Enables emulator to handle ESXDOS calls");
			menu_add_item_menu_ayuda(array_menu_storage,"Enables emulator to handle ESXDOS calls and "
				"use local files from your computer instead of using from inside the mmc/ide virtual file.\n"
				"You can choose to have also "
				"enabled mmc/ide virtual file or not, you can use one of the settings or both. In case of using both, supported esxdos handler "
				"functions are managed with it; if not, they will be handled by the usual mmc/ide virtual file firmware (usually esxdos).\n"
				"Use with caution, "
				"only some ESXDOS calls are handled, the rest are handled from the mmc/ide virtual file firmware.\n"
				"The list of supported calls are: \n"
				"DISK_FILEMAP, DISK_INFO, DISK_STATUS, F_CHDIR, F_CLOSE, F_FSTAT, F_GETCWD, F_MKDIR, F_MOUNT, F_OPEN, F_OPENDIR, F_READ, "
                "F_READDIR, F_RENAME, F_REWINDDIR, F_SEEK, F_SEEKDIR, F_STAT, F_SYNC, F_TELLDIR, F_UNLINK, F_WRITE, "
                "M_DOSVERSION, M_DRIVEINFO, M_GETSETDRV"
				"\n"
				"Note: you can also enable and disable ESXDOS handler whenever you want, for example in programs that use unsupported functions for handler."


			);
            menu_add_item_menu_tiene_submenu(array_menu_storage);
		}

		if (MACHINE_IS_SPECTRUM) {
            menu_add_item_menu_format(array_menu_storage,MENU_OPCION_NORMAL,menu_hilow,NULL,"~~HiLow Data Drive");
            menu_add_item_menu_shortcut(array_menu_storage,'h');
            menu_add_item_menu_tooltip(array_menu_storage,"HiLow settings");
            menu_add_item_menu_ayuda(array_menu_storage,"HiLow settings");
            menu_add_item_menu_tiene_submenu(array_menu_storage);
		}


		if (MACHINE_IS_SPECTRUM || MACHINE_IS_SAM) {

            menu_add_item_menu_format(array_menu_storage,MENU_OPCION_NORMAL,menu_ide_divide,NULL,"~~IDE");
            menu_add_item_menu_shortcut(array_menu_storage,'i');
            menu_add_item_menu_tooltip(array_menu_storage,"IDE, DivIDE and 8-bit simple settings");
            menu_add_item_menu_ayuda(array_menu_storage,"IDE, DivIDE and 8-bit simple settings");
            menu_add_item_menu_tiene_submenu(array_menu_storage);


		}



		if (MACHINE_IS_SPECTRUM || MACHINE_IS_CPC) {
            menu_add_item_menu_format(array_menu_storage,MENU_OPCION_NORMAL,menu_ifrom,NULL,"i~~From");
            menu_add_item_menu_shortcut(array_menu_storage,'f');
            menu_add_item_menu_tooltip(array_menu_storage,"iFrom settings");
            menu_add_item_menu_ayuda(array_menu_storage,"iFrom settings");
            menu_add_item_menu_tiene_submenu(array_menu_storage);
		}

        if (MACHINE_IS_SPECTRUM_16_48 || MACHINE_IS_SPECTRUM_128_P2) {
            menu_add_item_menu_format(array_menu_storage,MENU_OPCION_NORMAL,menu_interface1,NULL,"IF1/~~Microdrive");
            menu_add_item_menu_shortcut(array_menu_storage,'m');
            menu_add_item_menu_tooltip(array_menu_storage,"Interface1 and Microdrive settings");
            menu_add_item_menu_ayuda(array_menu_storage,"Interface1 and Microdrive settings");
            menu_add_item_menu_tiene_submenu(array_menu_storage);
		}

		if (MACHINE_IS_SPECTRUM || MACHINE_IS_CPC) {
            menu_add_item_menu_format(array_menu_storage,MENU_OPCION_NORMAL,menu_kartusho,NULL,"~~Kartusho");
            menu_add_item_menu_shortcut(array_menu_storage,'k');
            menu_add_item_menu_tooltip(array_menu_storage,"Kartusho settings");
            menu_add_item_menu_ayuda(array_menu_storage,"Kartusho settings");
            menu_add_item_menu_tiene_submenu(array_menu_storage);
		}

		if (MACHINE_IS_SPECTRUM) {
			//menu_tape_settings_trunc_name(mmc_file_name,string_mmc_file_shown,13);
			menu_add_item_menu_format(array_menu_storage,MENU_OPCION_NORMAL,menu_lec_memory,NULL,"LEC memory");
			menu_add_item_menu_tooltip(array_menu_storage,"LEC Memory extension");
			menu_add_item_menu_ayuda(array_menu_storage,"LEC Memory extension");
            menu_add_item_menu_tiene_submenu(array_menu_storage);
		}


		if (MACHINE_IS_SPECTRUM) {
			//menu_tape_settings_trunc_name(mmc_file_name,string_mmc_file_shown,13);
			menu_add_item_menu_format(array_menu_storage,MENU_OPCION_NORMAL,menu_mmc_divmmc,NULL,"~~SD/MMC");
			menu_add_item_menu_shortcut(array_menu_storage,'s');
			menu_add_item_menu_tooltip(array_menu_storage,"SD, MMC, DivMMC and ZXMMC settings");
			menu_add_item_menu_ayuda(array_menu_storage,"SD, MMC, DivMMC and ZXMMC settings");
            menu_add_item_menu_tiene_submenu(array_menu_storage);
		}


	    if (MACHINE_IS_SPECTRUM_48) {
            menu_add_item_menu_format(array_menu_storage,MENU_OPCION_NORMAL,menu_samram,NULL,"Samram");
            //menu_add_item_menu_shortcut(array_menu_storage,'s');

            menu_add_item_menu_tooltip(array_menu_storage,"Samram settings");
            menu_add_item_menu_ayuda(array_menu_storage,"Settings for Gerton Lunter Samram emulation.\n"
                    "It's a device that Gerton Lunter created and also was emulated in his own emulator\n"
                    "The original interface has a 32kb of static RAM and also uses 32 kb of the extra shadow unused faulty "
                    "ram of all Spectrum 48kb; in ZEsarUX I emulate it as a 64kb interface"
                    );
            menu_add_item_menu_tiene_submenu(array_menu_storage);
        }


		if (superupgrade_supported_machine() ) {
            menu_add_item_menu_format(array_menu_storage,MENU_OPCION_NORMAL,menu_superupgrade,NULL,"Speccy Superup~~grade");
            menu_add_item_menu_shortcut(array_menu_storage,'g');
            menu_add_item_menu_tooltip(array_menu_storage,"Superupgrade settings");
            menu_add_item_menu_ayuda(array_menu_storage,"Superupgrade settings");
            menu_add_item_menu_tiene_submenu(array_menu_storage);

		}




        if ( (MACHINE_IS_SPECTRUM || MACHINE_IS_CPC) && !MACHINE_IS_ZXUNO) {

            menu_add_item_menu_format(array_menu_storage,MENU_OPCION_NORMAL,menu_dandanator,NULL,
                "%s D~~andanator",(MACHINE_IS_SPECTRUM ? "ZX" : "CPC")  );
            menu_add_item_menu_shortcut(array_menu_storage,'a');
            menu_add_item_menu_tooltip(array_menu_storage,"Dandanator settings");
            menu_add_item_menu_ayuda(array_menu_storage,"Dandanator settings");
            menu_add_item_menu_tiene_submenu(array_menu_storage);

        }


		if (MACHINE_IS_SPECTRUM) {
			menu_add_item_menu_format(array_menu_storage,MENU_OPCION_NORMAL,menu_zxmmcplus,NULL,"ZXMMC+");
			menu_add_item_menu_tooltip(array_menu_storage,"ZXMMC+ settings");
			menu_add_item_menu_ayuda(array_menu_storage,"ZXMMC+ settings");
            menu_add_item_menu_tiene_submenu(array_menu_storage);
		}


		if (MACHINE_IS_ZX8081) {
			menu_add_item_menu_format(array_menu_storage,MENU_OPCION_NORMAL,menu_zxpand,NULL,"ZX~~pand emulation");
            menu_add_item_menu_shortcut(array_menu_storage,'p');
			menu_add_item_menu_tooltip(array_menu_storage,"ZXpand emulation");
			menu_add_item_menu_ayuda(array_menu_storage,"ZXpand emulation");
            menu_add_item_menu_tiene_submenu(array_menu_storage);

		}


		if (MACHINE_IS_ZXUNO) {
			menu_add_item_menu_format(array_menu_storage,MENU_OPCION_NORMAL,menu_zxuno_spi_flash,NULL,"~~ZX-Uno Flash");
			menu_add_item_menu_shortcut(array_menu_storage,'z');
            menu_add_item_menu_tiene_submenu(array_menu_storage);
		}

        if (MACHINE_IS_SPECTRUM) {
            menu_add_item_menu(array_menu_storage,"",MENU_OPCION_SEPARADOR,NULL,NULL);
            menu_add_item_menu_format(array_menu_storage,MENU_OPCION_NORMAL,menu_storage_copy_devices,NULL,"C~~opy Interfaces");
            menu_add_item_menu_shortcut(array_menu_storage,'o');
            menu_add_item_menu_tiene_submenu(array_menu_storage);
        }


        menu_add_item_menu(array_menu_storage,"",MENU_OPCION_SEPARADOR,NULL,NULL);
        //menu_add_item_menu(array_menu_storage,"ESC Back",MENU_OPCION_NORMAL|MENU_OPCION_ESC,NULL,NULL);
        menu_add_ESC_item(array_menu_storage);

        menu_add_item_menu_index_full_path(array_menu_storage,"Main Menu-> Storage","Menú Principal-> Almacenamiento","Menú Principal-> Emmagatzematge");

        retorno_menu=menu_dibuja_menu(&storage_opcion_seleccionada,&item_seleccionado,array_menu_storage,"Storage Menu","Menú Almacenamiento","Menú Emmagatzematge" );


        if ((item_seleccionado.tipo_opcion&MENU_OPCION_ESC)==0 && retorno_menu>=0) {
                //llamamos por valor de funcion
                if (item_seleccionado.menu_funcion!=NULL) {
                        //printf ("actuamos por funcion\n");
                        item_seleccionado.menu_funcion(item_seleccionado.valor_opcion);

                }
        }

    } while ( (item_seleccionado.tipo_opcion&MENU_OPCION_ESC)==0 && retorno_menu!=MENU_RETORNO_ESC && !salir_todos_menus);


}

void menu_toy_follow_mouse_draw_iris(zxvision_window *w,int x,int y,int radio,int color)
{
    int i;

    for (i=0;i<=radio;i++) {
        zxvision_draw_ellipse(w,x,y,i,i,color,zxvision_putpixel,360);
    }
}

void menu_toy_follow_mouse_draw_one_eye(zxvision_window *w,int origen_linea_x,int origen_linea_y,int delta_x,int delta_y,
    int *last_final_linea_x,int *last_final_linea_y,int max_tamanyo_usable)
{

    //printf("\nDeltas %d %d\n",delta_x,delta_y);
    z80_64bit cuadrado_a=delta_x*delta_x;
    z80_64bit cuadrado_b=delta_y*delta_y;
    z80_64bit cuadrados=(cuadrado_a) + (cuadrado_b);
    int hipotenusa=util_sqrt(cuadrados,NULL);

    //printf("cuadrados %lld %lld. suma cuadrados: %lld hipotenusa: %d\n",cuadrado_a,cuadrado_b,cuadrados,hipotenusa);
    int grado;

    if (hipotenusa==0) {
        grado=0;
    }
    else {
        grado=util_get_acosine((10000*delta_x)/hipotenusa);
    }

    //desplazamiento y va al reves. esto es "para abajo"
    if (delta_y>0) grado=360-grado;

    //printf("grado %d\n",grado);


    int max_radio_ojo=max_tamanyo_usable/2;

    int grosor_iris=5;

    //radio iris segun ojo
    int radio_iris=max_radio_ojo/4;



    int longitud_final_linea=max_radio_ojo-radio_iris-grosor_iris;

    //TODO: esto no va fino en caso de zoom_x!=zoom_y
    //Tampoco la distancia esta bien calculada, creo... habria que considerar hipotenusas y demas, y para el caso
    //de algo que es solo un juguete, no hace falta complicarse mas
    if (longitud_final_linea>hipotenusa/zoom_x/menu_gui_zoom) {
        //acortar si esta cerca
        longitud_final_linea=hipotenusa/zoom_x/menu_gui_zoom;
    }

    //calculo segun grado
    int final_linea_x=origen_linea_x+(longitud_final_linea*util_get_cosine(grado)/10000);
    int final_linea_y=origen_linea_y-(longitud_final_linea*util_get_sine(grado)/10000);


    //printf("x %d max_radio_ojo %d\n",origen_linea_x,max_radio_ojo);


    //if ((*last_final_linea_x)!=final_linea_x || (*last_final_linea_y) != final_linea_y) {

        //Muestro el contador segundo por si salen muchos seguidos y hace scroll para que se vea la actividad
        //En caso contrario seria el mismo mensaje repetido y por tanto no se veria scroll ni movimiento
        debug_printf(VERBOSE_DEBUG,"Redrawing eye. Seconds counter: %d",contador_segundo);


        //printf("Redrawing eye. Seconds counter: %d\n",contador_segundo);

        //El iris
        //Erase last
        menu_toy_follow_mouse_draw_iris(w,*last_final_linea_x,*last_final_linea_y,radio_iris,ESTILO_GUI_PAPEL_NORMAL);

        menu_toy_follow_mouse_draw_iris(w,final_linea_x,final_linea_y,radio_iris,ESTILO_GUI_TINTA_NORMAL);

        //El ojo
        //Ligera elipse

        int radio_ojo;


        //En incrementos de 0.1 para poder rellenar la elipse
        max_radio_ojo *=10;

        //max_radio_ojo-1 para no llegar al marco de la ventana
        for (radio_ojo=max_radio_ojo-1;radio_ojo>max_radio_ojo-grosor_iris*10;radio_ojo--) {

            //Ligeramente alargado
            int radio_vertical=(radio_ojo*5)/3;
            zxvision_draw_ellipse(w,origen_linea_x,origen_linea_y,radio_ojo/10,radio_vertical/10,ESTILO_GUI_TINTA_NORMAL,zxvision_putpixel,360);
        }

        *last_final_linea_x=final_linea_x;
        *last_final_linea_y=final_linea_y;

    //}

}



zxvision_window *menu_toy_follow_mouse_window;

int menu_toy_follow_last_eye1_x=0;
int menu_toy_follow_last_eye1_y=0;

int menu_toy_follow_last_eye2_x=0;
int menu_toy_follow_last_eye2_y=0;

int menu_toy_follow_last_mouse_x=-1;
int menu_toy_follow_last_mouse_y=-1;


void menu_toy_follow_mouse_overlay(void)
{



    menu_speech_set_tecla_pulsada(); //Si no, envia continuamente todo ese texto a speech

    //si ventana minimizada, no ejecutar todo el codigo de overlay
    if (menu_toy_follow_mouse_window->is_minimized) return;


    //Si no se mueve raton respecto a anterior, no hacer nada
    if (menu_toy_follow_last_mouse_x==mouse_x && menu_toy_follow_last_mouse_y==mouse_y) return;

    menu_toy_follow_last_mouse_x=mouse_x;
    menu_toy_follow_last_mouse_y=mouse_y;


    zxvision_window *w=menu_toy_follow_mouse_window;

    //printf("mouse    %d x %d\n",mouse_x,mouse_y);



    //Para que quepan los dos ojos, dividir entre 3
    int origen_linea_x=(w->visible_width)*menu_char_width/4;

    //printf("Ancho: %d (%d) origen_x: %d\n",w->visible_width,(w->visible_width)*menu_char_width,origen_linea_x);

    int origen_linea_y=(w->visible_height-1)*menu_char_height/2;
    int max_tamanyo_usable;


    max_tamanyo_usable=(w->visible_width)*menu_char_width;


    //para que quepan dos ojos
    max_tamanyo_usable /=2;

    if (max_tamanyo_usable<10) max_tamanyo_usable=10;


    //this window

    int this_win_y=(w->y)*menu_char_height*menu_gui_zoom*zoom_y+((origen_linea_y+menu_char_height)*zoom_y*menu_gui_zoom); //menu_char_height de la linea titulo
    int delta_y=mouse_y-this_win_y;


    int this_win_x=(w->x)*menu_char_width*menu_gui_zoom*zoom_x+(origen_linea_x*zoom_x*menu_gui_zoom);

    int delta_x=mouse_x-this_win_x;

    //printf("1 this win %d x %d\n",this_win_x,this_win_y);
    //printf("1 delta %d , %d\n",delta_x,delta_y);



    menu_toy_follow_mouse_draw_one_eye(w,origen_linea_x,origen_linea_y,delta_x,delta_y,
        &menu_toy_follow_last_eye1_x,&menu_toy_follow_last_eye1_y,max_tamanyo_usable);



    //segundo ojo
    origen_linea_x +=max_tamanyo_usable;

    this_win_x=(w->x)*menu_char_width*menu_gui_zoom*zoom_x+(origen_linea_x*zoom_x*menu_gui_zoom);
    delta_x=mouse_x-this_win_x;

    //printf("2 this win %d x %d\n",this_win_x,this_win_y);
    //printf("2 delta %d , %d\n",delta_x,delta_y);


    menu_toy_follow_mouse_draw_one_eye(w,origen_linea_x,origen_linea_y,delta_x,delta_y,
        &menu_toy_follow_last_eye2_x,&menu_toy_follow_last_eye2_y,max_tamanyo_usable);



    zxvision_draw_window_contents(menu_toy_follow_mouse_window);

}




//Almacenar la estructura de ventana aqui para que se pueda referenciar desde otros sitios
zxvision_window zxvision_window_toy_follow_mouse;


void menu_toy_follow_mouse(MENU_ITEM_PARAMETERS)
{
	menu_espera_no_tecla();

    if (!menu_multitarea) {
        menu_warn_message("This window needs multitask enabled");
        return;
    }

    zxvision_window *ventana;
    ventana=&zxvision_window_toy_follow_mouse;

	//IMPORTANTE! no crear ventana si ya existe. Esto hay que hacerlo en todas las ventanas que permiten background.
	//si no se hiciera, se crearia la misma ventana, y en la lista de ventanas activas , al redibujarse,
	//la primera ventana repetida apuntaria a la segunda, que es el mismo puntero, y redibujaria la misma, y se quedaria en bucle colgado
	//zxvision_delete_window_if_exists(ventana);

    //Crear ventana si no existe
    if (!zxvision_if_window_already_exists(ventana)) {

        int xventana,yventana,ancho_ventana,alto_ventana,is_minimized,is_maximized,ancho_antes_minimize,alto_antes_minimize;

        if (!util_find_window_geometry("toyzxeyes",&xventana,&yventana,&ancho_ventana,&alto_ventana,&is_minimized,&is_maximized,&ancho_antes_minimize,&alto_antes_minimize)) {
            ancho_ventana=19;
            alto_ventana=19;

            xventana=menu_center_x()-ancho_ventana/2;
            yventana=menu_center_y()-alto_ventana/2;
        }


        zxvision_new_window_gn_cim(ventana,xventana,yventana,ancho_ventana,alto_ventana,ancho_ventana-1,alto_ventana-2,"ZXEyes",
            "toyzxeyes",is_minimized,is_maximized,ancho_antes_minimize,alto_antes_minimize);

        ventana->can_be_backgrounded=1;

        ventana->can_use_all_width=1;

    }

    //Si ya existe, activar esta ventana
    else {
        //Quitando el overlay de dicha ventana para que no se redibuje dos veces (con su overlay y luego con draw below windows)
        //TODO: esto en un futuro probablemente se hara el redibujado desde draw below cuando esta activa, por tanto este NULL no se pondra
        ventana->overlay_function=NULL;

        zxvision_activate_this_window(ventana);
    }


zxvision_draw_window(ventana);

    //cambio overlay

    menu_toy_follow_mouse_window=ventana; //Decimos que el overlay lo hace sobre la ventana que tenemos aqui
    zxvision_set_window_overlay(ventana,menu_toy_follow_mouse_overlay);


	z80_byte tecla;


	int salir=0;






    //Toda ventana que este listada en zxvision_known_window_names_array debe permitir poder salir desde aqui
    //Se sale despues de haber inicializado overlay y de cualquier otra variable que necesite el overlay
    if (zxvision_currently_restoring_windows_on_start) {
            //printf ("Saliendo de ventana ya que la estamos restaurando en startup\n");
            return;
    }

    do {


		tecla=zxvision_common_getkey_refresh();


        switch (tecla) {

            case 11:
                //arriba
                //blablabla
            break;



            //Salir con ESC
            case 2:
                salir=1;
            break;

            //O tecla background
            case 3:
                salir=1;
            break;
        }


    } while (salir==0);




	util_add_window_geometry_compact(ventana);

	if (tecla==3) {
		zxvision_message_put_window_background();
	}

	else {

		zxvision_destroy_window(ventana);
	}


}


#define GAMELIFE_MAX_WIDTH 120
#define GAMELIFE_MAX_HEIGHT 80
int gamelife_board[GAMELIFE_MAX_WIDTH][GAMELIFE_MAX_HEIGHT];

//Usado temporalmente para el siguiente ciclo
int gamelife_board_temporal_copy[GAMELIFE_MAX_WIDTH][GAMELIFE_MAX_HEIGHT];

//Usado para solo redibujar casillas que hayan cambiado
int gamelife_board_last_state[GAMELIFE_MAX_WIDTH][GAMELIFE_MAX_HEIGHT];

//cuantos pixeles representa cada casilla
#define GAMELIFE_SIZE_LIVE 4

#define GAMELIFE_INITIAL_WIDTH 80
#define GAMELIVE_INITIAL_HEIGHT 40
#define GAMELIVE_LINES_MENU 1

int gamelife_current_width=GAMELIFE_INITIAL_WIDTH;
int gamelife_current_height=GAMELIVE_INITIAL_HEIGHT;

int gamelife_started=0;


int gamelife_timer_counter=0;
int last_gamelife_timer_counter=0;

//Inicia pausado
int gamelife_paused=1;

//Si usa rejilla
int gamelife_grid=0;

#define GAMELIFE_COLOR_BACKGROUND 0
#define GAMELIFE_COLOR_ALIVE 2+8
#define GAMELIFE_COLOR_DEAD 7

void gamelife_copy_board_state(void)
{
    int x,y;

    for (x=0;x<gamelife_current_width;x++) {
        for (y=0;y<gamelife_current_height;y++) {
            gamelife_board_last_state[x][y]=gamelife_board[x][y];
        }
    }
}

void gamelife_reset_last_board_state(void)
{
    int x,y;

    for (x=0;x<gamelife_current_width;x++) {
        for (y=0;y<gamelife_current_height;y++) {
            //cualquier cosa que no vaya a ser lo anterior
            gamelife_board_last_state[x][y]=-1;
        }
    }
}

int gamelife_empty_board(void)
{


        int x,y;

        for (x=0;x<gamelife_current_width;x++) {
            for (y=0;y<gamelife_current_height;y++) {

                if (gamelife_board[x][y]) return 0;
            }
        }

    return 1;

}

int gamelife_get_neighbors_fila_arriba_abajo(int x,int y)
{
    int vecinos=0;

    /*
      xxx
    */

   //izqu
   if (x>=1) vecinos+=gamelife_board[x-1][y];
   //centro
   vecinos+=gamelife_board[x][y];
   //derecha
   if (x<gamelife_current_width-1) vecinos+=gamelife_board[x+1][y];

   return vecinos;

}

int gamelife_get_neighbors(int x,int y)
{

    int vecinos=0;

    /*

        xxx
        xOx
        xxx
    */

    //fila de arriba
    if (y>=1) vecinos +=gamelife_get_neighbors_fila_arriba_abajo(x,y-1);


    //fila de abajo
    if (y<gamelife_current_height-1) vecinos +=gamelife_get_neighbors_fila_arriba_abajo(x,y+1);

   //mi fila
   //izqu
   if (x>=1) vecinos+=gamelife_board[x-1][y];
   //derecha
   if (x<gamelife_current_width-1) vecinos+=gamelife_board[x+1][y];

   return vecinos;

}




void gamelife_next_generation(void)
{
/*
Nace: Si una célula muerta tiene exactamente 3 células vecinas vivas "nace" (es decir, al turno siguiente estará viva).
Muere: una célula viva puede morir por uno de 2 casos:
Sobrepoblación: si tiene más de tres vecinos alrededor.
Aislamiento: si tiene solo un vecino alrededor o ninguno.
Vive: una célula se mantiene viva si tiene 2 o 3 vecinos a su alrededor.
*/

    //Nuevas generaciones 1 cada segundo
    if (last_gamelife_timer_counter==gamelife_timer_counter) return;

    if (gamelife_paused) return;

    debug_printf(VERBOSE_DEBUG,"Next gamelife generation");

    last_gamelife_timer_counter=gamelife_timer_counter;

    //Y si se ha pulsado cursor arriba o abajo
    //if (!gamelife_next_event) return;

    //gamelife_next_event=0;

    int x,y;

    for (x=0;x<gamelife_current_width;x++) {
        for (y=0;y<gamelife_current_height;y++) {
            //randomize board
            int alive=gamelife_board[x][y];

            int neighbors=gamelife_get_neighbors(x,y);

            if (!alive) {
                if (neighbors==3) alive=1;
            }
            else {
                if (neighbors>3 || neighbors<=1) alive=0;
                //en caso contrario se mantiene viva
            }

            //printf("Cycle %d,%d=%d\n",x,y,alive);

            //gamelife_board[x][y]=alive;
            gamelife_board_temporal_copy[x][y]=alive;
        }
    }

    //Y copiar del tablero temporal del siguiente ciclo a este
    for (x=0;x<gamelife_current_width;x++) {
        for (y=0;y<gamelife_current_height;y++) {
            gamelife_board[x][y]=gamelife_board_temporal_copy[x][y];
        }
    }
}

void gamelife_clear_board(void)
{
    int x,y;

    //Inicializarlo siempre todo el maximo
    for (x=0;x<GAMELIFE_MAX_WIDTH;x++) {
        for (y=0;y<GAMELIFE_MAX_HEIGHT;y++) {
            gamelife_board[x][y]=0;

        }
    }
}

void gamelife_random_board(void)
{


    debug_printf(VERBOSE_DEBUG,"Generating new gamelife. random noise: %d",util_random_noise);


    int x,y;

    for (x=0;x<gamelife_current_width;x++) {
        for (y=0;y<gamelife_current_height;y++) {
            //randomize board
            int alive;
            ay_randomize(0);

            int valor_random=randomize_noise[0];

            //un poco mas aleatorio
            //como util_random_noise es valor en ms de tiempo pulsado tecla o raton, habitualmente
            //esto ira de 0 a 1000
            valor_random +=util_random_noise;

            //50% de probabilidad vivo / muerto
            if (valor_random % 2000 < 1000) alive=1;
            else alive=0;

            //printf("Randomize board %d,%d=%d\n",x,y,alive);

            gamelife_board[x][y]=alive;
        }
    }



}

void gamelife_next_frame(zxvision_window *w)
{

    //Si tablero no inicializado o si tablero vacio
    /*if (gamelife_empty_board()) gamelife_started=0;

    if (!gamelife_started) {
        gamelife_started=1;

        gamelife_new_board();
    }

    else {
        gamelife_next_generation();
    }*/

    if (gamelife_empty_board()) {
        if (!gamelife_paused) {
            gamelife_paused=1;
            //Indicarlo para que el usuario lo vea
            zxvision_print_string_defaults_fillspc(w,1,0,"Ended");
        }
       return;
    }

    gamelife_next_generation();

}



//int gamelife_next_event=0;

//si dibujar en el fondo con menu cerrado
//int gamelife_draw_with_menu_closed=0;





int menu_toy_zxlife_contador_segundo_anterior;

zxvision_window *menu_toy_zxlife_window;

//forzar dibujado del siguiente overlay, para cuando se edita
int menu_toy_zxlife_draw_force_overlay=0;

void menu_toy_zxlife_draw_life(zxvision_window *w,int x,int y,int alive)
{

    int color;

    if (alive) color=GAMELIFE_COLOR_ALIVE;
    else color=GAMELIFE_COLOR_DEAD;



    x *=GAMELIFE_SIZE_LIVE;
    y *=GAMELIFE_SIZE_LIVE;


    y +=GAMELIVE_LINES_MENU*menu_char_height;

    //1 columna mas a la izquierda para que no toque en el margen izquierdo
    x +=menu_char_width;

    //Aumentar en tantos pixeles como GAMELIFE_SIZE_LIVE
    int incx,incy;

    for (incx=0;incx<GAMELIFE_SIZE_LIVE;incx++) {
        for (incy=0;incy<GAMELIFE_SIZE_LIVE;incy++) {
            int color_final=color;

            //Dibujar cuadricula
            if (gamelife_grid) {
                if (incx==0 || incy==0) color_final=GAMELIFE_COLOR_BACKGROUND;
            }

            zxvision_putpixel(w,x+incx,y+incy,color_final);
        }
    }




}

//Forzar a recalcular la primera vez
int zxlife_last_window_width=-1;
int zxlife_last_window_height=-1;

void menu_toy_zxlife_overlay(void)
{



    menu_speech_set_tecla_pulsada(); //Si no, envia continuamente todo ese texto a speech

    //si ventana minimizada, no ejecutar todo el codigo de overlay
    if (menu_toy_zxlife_window->is_minimized) return;

    //printf("%d %d %d\n",contador_segundo_infinito,contador_segundo%500,menu_toy_zxlife_contador_segundo_anterior);

    //esto hara ejecutar esto 2 veces por segundo
    //no hacemos 1 vez por segundo (tal y como hace los nuevos ciclos de vida) porque solo con que estemos
    //dibujando a 49 FPS en vez de 50, no se dibujaria nunca
    //aqui mejor se compara contra contador_segundo_infinito porque contador_segundo coincide muchas veces con el
    //contador anterior y entonces refresca menos de 2 veces por segundo
    if (
        ((contador_segundo%500) == 0 && menu_toy_zxlife_contador_segundo_anterior!=contador_segundo_infinito) ||
        menu_toy_zxlife_draw_force_overlay

    )

    {
        menu_toy_zxlife_draw_force_overlay=0;
        menu_toy_zxlife_contador_segundo_anterior=contador_segundo_infinito;
        //printf("Draw\n");


        //Recalcular ancho y alto segun tamaño ventana
        if (menu_toy_zxlife_window->visible_width!=zxlife_last_window_width
        ||  menu_toy_zxlife_window->visible_height!=zxlife_last_window_height
        )
        {
            debug_printf(VERBOSE_DEBUG,"Recalculate game of life size");

            zxlife_last_window_width=menu_toy_zxlife_window->visible_width;
            zxlife_last_window_height=menu_toy_zxlife_window->visible_height;

            int ancho=((zxlife_last_window_width-2)*menu_char_width)/GAMELIFE_SIZE_LIVE;

            int alto=((zxlife_last_window_height-GAMELIVE_LINES_MENU-3)*menu_char_height)/GAMELIFE_SIZE_LIVE;

            //vigilar limites
            if (ancho>GAMELIFE_MAX_WIDTH) ancho=GAMELIFE_MAX_WIDTH;

            if (alto>GAMELIFE_MAX_HEIGHT) alto=GAMELIFE_MAX_HEIGHT;

            if (ancho<4) ancho=4;
            if (alto<4) alto=4;

            gamelife_current_width=ancho;
            gamelife_current_height=alto;

            debug_printf(VERBOSE_DEBUG,"New board size: %d x %d",gamelife_current_width,gamelife_current_height);

        }

        //Siguiente ciclo life

        //gamelife_timer_background_activity();

        gamelife_next_frame(menu_toy_zxlife_window);


        int x,y;

        //debug. cuando hay que redibujar todos los pixeles
        if (menu_toy_zxlife_window->dirty_user_must_draw_contents) {
            debug_printf(VERBOSE_DEBUG,"Drawing all fields on ZX Life because the window is dirty");
        }

        for (x=0;x<gamelife_current_width;x++) {
            for (y=0;y<gamelife_current_height;y++) {

                int alive=gamelife_board[x][y];

                //dibujar solo si cambia (o si esta toda la ventana dirty por haberse movido, redimensionado, etc)
                //Con esto pasamos de usar 20ms a usar 4ms , en un tablero random inicial, de 120x78 casillas

                int last_alive=gamelife_board_last_state[x][y];

                if (alive!=last_alive || menu_toy_zxlife_window->dirty_user_must_draw_contents) {
                    menu_toy_zxlife_draw_life(menu_toy_zxlife_window,x,y,alive);
                }
            }
        }

        gamelife_copy_board_state();

        menu_toy_zxlife_window->dirty_user_must_draw_contents=0;

    }


    zxvision_draw_window_contents(menu_toy_zxlife_window);

}



void menu_toys_zxlife_random(MENU_ITEM_PARAMETERS)
{
    gamelife_random_board();
    gamelife_reset_last_board_state();
    menu_toy_zxlife_draw_force_overlay=1;
}


void menu_toys_zxlife_pause(MENU_ITEM_PARAMETERS)
{
    gamelife_paused ^=1;
    menu_toy_zxlife_draw_force_overlay=1;
}

void menu_toys_zxlife_grid(MENU_ITEM_PARAMETERS)
{
    gamelife_grid ^=1;
    gamelife_reset_last_board_state();
    menu_toy_zxlife_draw_force_overlay=1;
}

//Almacenar la estructura de ventana aqui para que se pueda referenciar desde otros sitios
zxvision_window zxvision_window_toy_zxlife;



int menu_toys_zxlife_mouse_en_ventana(zxvision_window *w)
{
    if (zxvision_current_window==w && si_menu_mouse_en_ventana()) return 1;
    else return 0;
}

int menu_toys_zxlife_ultimo_pulsado_posicion_x=-1;
int menu_toys_zxlife_ultimo_pulsado_posicion_y=-1;
int menu_toys_zxlife_ultimo_pulsado_posicion_alive=-1;

void menu_toys_zxlife_handle_click_position(int pulsado_x,int pulsado_y,int alive)
{
    //Restar primera columna y lineas menu
    pulsado_y -=GAMELIVE_LINES_MENU*menu_char_height;

    pulsado_x -=menu_char_width;

    //Obtener coordenadas segun tamaño de cada celda
    pulsado_x /=GAMELIFE_SIZE_LIVE;
    pulsado_y /=GAMELIFE_SIZE_LIVE;

    if (pulsado_x>=0 && pulsado_y>=0 && pulsado_x<gamelife_current_width && pulsado_y<gamelife_current_height) {
        //printf("Conmutar posicion %d,%d\n",pulsado_x,pulsado_y);

        //Pero si era la misma posicion y estado que antes, no repetir
        if (
            menu_toys_zxlife_ultimo_pulsado_posicion_x!=pulsado_x ||
            menu_toys_zxlife_ultimo_pulsado_posicion_y!=pulsado_y ||
            menu_toys_zxlife_ultimo_pulsado_posicion_alive!=alive

        )
        {
            //printf("Poner posicion %d,%d a %d\n",pulsado_x,pulsado_y,alive);
            menu_toys_zxlife_ultimo_pulsado_posicion_x=pulsado_x;
            menu_toys_zxlife_ultimo_pulsado_posicion_y=pulsado_y;
            menu_toys_zxlife_ultimo_pulsado_posicion_alive=alive;
            gamelife_board[pulsado_x][pulsado_y]=alive;
            menu_toy_zxlife_draw_force_overlay=1;
        }
    }

}



void menu_toys_zxlife_clear(MENU_ITEM_PARAMETERS)
{
    gamelife_clear_board();
    menu_toy_zxlife_draw_force_overlay=1;
}

int menu_toys_zxlife_edit_salido_background=0;

void menu_toys_zxlife_edit(MENU_ITEM_PARAMETERS)
{

    zxvision_window *ventana;
    ventana=&zxvision_window_toy_zxlife;

    //Limpiar primera linea
    zxvision_print_string_defaults_fillspc(ventana,1,0,"Edit mode. Use mouse buttons. ESC return");

	z80_byte tecla;


	int salir=0;

    menu_toys_zxlife_edit_salido_background=0;

    do {


		tecla=zxvision_common_getkey_refresh();


        switch (tecla) {

            //Salir con ESC
            case 2:
                salir=1;
            break;

            //O tecla background
            case 3:
                salir=1;
                menu_toys_zxlife_edit_salido_background=1;
            break;

            case 0:

                if (mouse_left && menu_toys_zxlife_mouse_en_ventana(ventana) /*&& !mouse_is_dragging*/) {
                    int pulsado_x,pulsado_y;
                    zxvision_get_mouse_in_window(ventana,&pulsado_x,&pulsado_y);
                    //printf("Pulsado left: %d,%d\n",pulsado_x,pulsado_y);

                    menu_toys_zxlife_handle_click_position(pulsado_x,pulsado_y,1);

                    //menu_espera_no_tecla();
                }

                if (mouse_right && menu_toys_zxlife_mouse_en_ventana(ventana) /*&& !mouse_is_dragging*/) {
                    int pulsado_x,pulsado_y;
                    zxvision_get_mouse_in_window(ventana,&pulsado_x,&pulsado_y);
                    //printf("Pulsado right: %d,%d\n",pulsado_x,pulsado_y);

                    menu_toys_zxlife_handle_click_position(pulsado_x,pulsado_y,0);

                    //menu_espera_no_tecla();
                }


            break;
        }


    } while (salir==0);


    //Limpiar primera linea
    zxvision_print_string_defaults_fillspc(ventana,1,0,"");


}

void menu_toys_zxlife(MENU_ITEM_PARAMETERS)
{
	menu_espera_no_tecla();

    if (!menu_multitarea) {
        menu_warn_message("This window needs multitask enabled");
        return;
    }

    if (!gamelife_started)  {
        gamelife_started=1;

        gamelife_clear_board();
    }


    zxvision_window *ventana;
    ventana=&zxvision_window_toy_zxlife;

	//IMPORTANTE! no crear ventana si ya existe. Esto hay que hacerlo en todas las ventanas que permiten background.
	//si no se hiciera, se crearia la misma ventana, y en la lista de ventanas activas , al redibujarse,
	//la primera ventana repetida apuntaria a la segunda, que es el mismo puntero, y redibujaria la misma, y se quedaria en bucle colgado
	//zxvision_delete_window_if_exists(ventana);

    //Crear ventana si no existe
    if (!zxvision_if_window_already_exists(ventana)) {

        int xventana,yventana,ancho_ventana,alto_ventana,is_minimized,is_maximized,ancho_antes_minimize,alto_antes_minimize;

        if (!util_find_window_geometry("zxlife",&xventana,&yventana,&ancho_ventana,&alto_ventana,&is_minimized,&is_maximized,&ancho_antes_minimize,&alto_antes_minimize)) {
            ancho_ventana=(GAMELIFE_INITIAL_WIDTH*GAMELIFE_SIZE_LIVE)/menu_char_width +2 ;
            alto_ventana=(GAMELIVE_INITIAL_HEIGHT*GAMELIFE_SIZE_LIVE)/menu_char_height + GAMELIVE_LINES_MENU + 3;


            xventana=menu_center_x()-ancho_ventana/2;
            yventana=menu_center_y()-alto_ventana/2;
        }


        zxvision_new_window_gn_cim(ventana,xventana,yventana,ancho_ventana,alto_ventana,ancho_ventana-1,alto_ventana-2,"ZXLife",
            "zxlife",is_minimized,is_maximized,ancho_antes_minimize,alto_antes_minimize);

        ventana->can_be_backgrounded=1;

        ventana->can_use_all_width=1;

        //definir color de papel de fondo
        ventana->default_paper=GAMELIFE_COLOR_BACKGROUND;
        zxvision_cls(ventana);

    }

    //Si ya existe, activar esta ventana
    else {
        //Quitando el overlay de dicha ventana para que no se redibuje dos veces (con su overlay y luego con draw below windows)
        //TODO: esto en un futuro probablemente se hara el redibujado desde draw below cuando esta activa, por tanto este NULL no se pondra
        ventana->overlay_function=NULL;

        zxvision_activate_this_window(ventana);
    }

    //zxlife_last_window_width=ventana->visible_width;
    //zxlife_last_window_height=ventana->visible_height;

    zxvision_draw_window(ventana);

    //cambio overlay

    menu_toy_zxlife_window=ventana; //Decimos que el overlay lo hace sobre la ventana que tenemos aqui
    zxvision_set_window_overlay(ventana,menu_toy_zxlife_overlay);


    //menu_toy_zxlife_contador_segundo_anterior=contador_segundo;


    menu_toys_zxlife_edit_salido_background=0;


    //Toda ventana que este listada en zxvision_known_window_names_array debe permitir poder salir desde aqui
    //Se sale despues de haber inicializado overlay y de cualquier otra variable que necesite el overlay
    if (zxvision_currently_restoring_windows_on_start) {
            //printf ("Saliendo de ventana ya que la estamos restaurando en startup\n");
            return;
    }

    menu_item *array_menu_zxlife;
	menu_item item_seleccionado;
	int retorno_menu;
	do {


        int x=1;

		menu_add_item_menu_inicial_format(&array_menu_zxlife,MENU_OPCION_NORMAL,menu_toys_zxlife_pause,NULL,
            "[%c] ~~Paused",(gamelife_paused ? 'X' : ' '));
		menu_add_item_menu_shortcut(array_menu_zxlife,'p');
		menu_add_item_menu_ayuda(array_menu_zxlife,"Pause board");
		menu_add_item_menu_tabulado(array_menu_zxlife,x,0);
        x+=11;


        menu_add_item_menu_format(array_menu_zxlife,MENU_OPCION_NORMAL,menu_toys_zxlife_grid,NULL,
            "[%c] ~~Grid",(gamelife_grid ? 'X' : ' '));
		menu_add_item_menu_shortcut(array_menu_zxlife,'g');
		menu_add_item_menu_ayuda(array_menu_zxlife,"Toggle grid");
		menu_add_item_menu_tabulado(array_menu_zxlife,x,0);
        x+=9;

		menu_add_item_menu_format(array_menu_zxlife,MENU_OPCION_NORMAL,menu_toys_zxlife_edit,NULL,
            "~~Edit");
		menu_add_item_menu_shortcut(array_menu_zxlife,'e');
		menu_add_item_menu_ayuda(array_menu_zxlife,"Edit board");
		menu_add_item_menu_tabulado(array_menu_zxlife,x,0);
        x+=5;


		menu_add_item_menu_format(array_menu_zxlife,MENU_OPCION_NORMAL,menu_toys_zxlife_random,NULL,"~~Random");
		menu_add_item_menu_shortcut(array_menu_zxlife,'r');
		menu_add_item_menu_ayuda(array_menu_zxlife,"Randomize board");
		menu_add_item_menu_tabulado(array_menu_zxlife,x,0);
        x+=7;


		menu_add_item_menu_format(array_menu_zxlife,MENU_OPCION_NORMAL,menu_toys_zxlife_clear,NULL,
            "~~Clear");
		menu_add_item_menu_shortcut(array_menu_zxlife,'c');
		menu_add_item_menu_ayuda(array_menu_zxlife,"Clear board");
		menu_add_item_menu_tabulado(array_menu_zxlife,x,0);
        x+=6;



		//Nombre de ventana solo aparece en el caso de stdout
		retorno_menu=menu_dibuja_menu_no_title_lang(&zxlife_opcion_seleccionada,&item_seleccionado,array_menu_zxlife,"ZXLife" );

		if (retorno_menu!=MENU_RETORNO_BACKGROUND) {
            //En caso de menus tabulados, es responsabilidad de este de borrar la ventana
            //Con este cls provoca que se borren todas las otras ventanas en background


            if ((item_seleccionado.tipo_opcion&MENU_OPCION_ESC)==0 && retorno_menu>=0) {
                    //llamamos por valor de funcion
                    if (item_seleccionado.menu_funcion!=NULL) {
                            //printf ("actuamos por funcion\n");
                            item_seleccionado.menu_funcion(item_seleccionado.valor_opcion);


                    }
            }

            if (menu_toys_zxlife_edit_salido_background) retorno_menu=MENU_RETORNO_BACKGROUND;

		}





	} while ( (item_seleccionado.tipo_opcion&MENU_OPCION_ESC)==0 && retorno_menu!=MENU_RETORNO_ESC && !salir_todos_menus && retorno_menu!=MENU_RETORNO_BACKGROUND);





	util_add_window_geometry_compact(ventana);

	if (retorno_menu==MENU_RETORNO_BACKGROUND) {
		zxvision_message_put_window_background();
	}

	else {

		zxvision_destroy_window(ventana);
	}




}

void menu_toys(MENU_ITEM_PARAMETERS)
{
    menu_item *array_menu_toys;
    menu_item item_seleccionado;
	int retorno_menu;

    //letras usadas: rhndmictxbsvfpoaug
    do {
        menu_add_item_menu_inicial(&array_menu_toys,"ZX~~Life",MENU_OPCION_NORMAL,menu_toys_zxlife,NULL);
        menu_add_item_menu_tooltip(array_menu_toys,"This my ZEsarUX own version of the Game of Life :)");
        menu_add_item_menu_ayuda(array_menu_toys,"The Game of Life, also known simply as Life, is a cellular automaton "
            "devised by the British mathematician John Horton Conway in 1970. It is a zero-player game, meaning "
            "that its evolution is determined by its initial state, requiring no further input. "
            "One interacts with the Game of Life by creating an initial configuration and observing how it evolves. "
            "It is Turing complete and can simulate a universal constructor or any other Turing machine. "
            "This my ZEsarUX own version of the Game of Life :)");
		menu_add_item_menu_shortcut(array_menu_toys,'l');
        menu_add_item_menu_se_cerrara(array_menu_toys);
        menu_add_item_menu_genera_ventana(array_menu_toys);



		menu_add_item_menu_en_es_ca(array_menu_toys,MENU_OPCION_NORMAL,menu_toy_follow_mouse,NULL,
            "ZX~~Eyes","ZX~~Eyes","ZX~~Eyes");
        menu_add_item_menu_tooltip(array_menu_toys,"This is my ZEsarUX own version of the Unix classic toy ;)");
        menu_add_item_menu_ayuda(array_menu_toys,"This is my ZEsarUX own version of the Unix classic toy ;)");
        menu_add_item_menu_shortcut(array_menu_toys,'e');
        menu_add_item_menu_se_cerrara(array_menu_toys);
        menu_add_item_menu_genera_ventana(array_menu_toys);




        menu_add_item_menu(array_menu_toys,"",MENU_OPCION_SEPARADOR,NULL,NULL);
                //menu_add_item_menu(array_menu_toys,"ESC Back",MENU_OPCION_NORMAL|MENU_OPCION_ESC,NULL,NULL);
		menu_add_ESC_item(array_menu_toys);

        menu_add_item_menu_index_full_path(array_menu_toys,"Main Menu-> Procrastinate","Menú Principal-> Procrastinar","Menú Principal-> Procrastinar");

        retorno_menu=menu_dibuja_menu(&toys_opcion_seleccionada,&item_seleccionado,array_menu_toys,
            "Procrastinate Menu","Menú Procrastinar","Menú Procrastinar");



		if ((item_seleccionado.tipo_opcion&MENU_OPCION_ESC)==0 && retorno_menu>=0) {
                        //llamamos por valor de funcion
                        if (item_seleccionado.menu_funcion!=NULL) {
                                //printf ("actuamos por funcion\n");
                                item_seleccionado.menu_funcion(item_seleccionado.valor_opcion);

                        }
                }

	} while ( (item_seleccionado.tipo_opcion&MENU_OPCION_ESC)==0 && retorno_menu!=MENU_RETORNO_ESC && !salir_todos_menus);

}






zxvision_window *menu_process_switcher_window;

void menu_process_switcher_draw_icon_text_putpixel(z80_int *destino GCC_UNUSED,int x,int y,int ancho GCC_UNUSED,int color GCC_UNUSED)
{
    //El color es el del estilo
    zxvision_putpixel(menu_process_switcher_window,x,y,ESTILO_GUI_TINTA_NORMAL);
}


void menu_process_switcher_draw_icon_putpixel(z80_int *destino GCC_UNUSED,int x,int y,int ancho GCC_UNUSED,int color)
{

    zxvision_putpixel(menu_process_switcher_window,x,y,color);
}

//Escribir texto del icono
void menu_process_switcher_draw_icon_text(int x,int y,char *texto,int max_texto)
{


    int zoom=1;

    //darle el nivel de zoom x o y, el que sea menor
    //if (zoom_x<zoom_y) zoom=zoom_x;
    //else zoom=zoom_y;

    int ancho_caracter=CHARSET_ICONS_ANCHO;
    int alto_caracter=CHARSET_ICONS_ALTO;


    while (*texto && max_texto>0) {
        unsigned char c=*texto;
        texto++;
        max_texto--;

        //if (ESTILO_GUI_SOLO_MAYUSCULAS) c=letra_mayuscula(c);

        //Fuerzo mayusculas porque se leen mejor
        c=letra_mayuscula(c);

        if (c<32 || c>126) c='?';

        int offset=(c-32)*alto_caracter;
        //printf("c %d offset %d\n",c,offset);


        //TODO: esta funcion no permite zoom X y zoom Y diferentes, solo permite uno
        //en el caso del texto le enviamos el zoom que sea mas pequeño
        //screen_put_asciibitmap_generic_offset_inicio(charset_icons_text,NULL,x,y,ancho_caracter,alto_caracter, 0,
        //    menu_process_switcher_draw_icon_putpixel,zoom,0,offset);

        screen_put_asciibitmap_generic_offset_inicio(charset_icons_text,NULL,x,y,ancho_caracter,alto_caracter,
            0,menu_process_switcher_draw_icon_text_putpixel,zoom,0,offset);


        x +=(ancho_caracter+1)*zoom;     //El ancho de caracter +1 para que no queden pegados

    }


}

//*1.75
#define PROCESS_SWITCHER_ICON_SEPARATION_X ((ZESARUX_ASCII_LOGO_ANCHO*175)/100)
#define PROCESS_SWITCHER_ICON_SEPARATION_Y ((ZESARUX_ASCII_LOGO_ANCHO*175)/100)


//Retorna las coordenadas del raton
void menu_process_switcher_calculate_mouse_xy_absolute_interface(int *resultado_x,int *resultado_y)
{
	int x,y;

	menu_calculate_mouse_xy_absolute_interface_pixel(&x,&y);


	//x /=menu_char_width;
	//y /=menu_char_height;

	//x /= menu_gui_zoom;
	//y /= menu_gui_zoom;

    //multiplicamos por zoom
    x *=zoom_x;
    y *=zoom_y;

	//printf ("antes de restar: %d,%d\n",x,y);
	*resultado_x=x;
	*resultado_y=y;

}


int menu_process_switcher_get_max_icons_per_row(zxvision_window *ventana)
{

    int window_width_pixels=ventana->visible_width*menu_char_width;
    int max_icons_per_row=window_width_pixels/PROCESS_SWITCHER_ICON_SEPARATION_X;

    if (max_icons_per_row==0) max_icons_per_row=1;

    return max_icons_per_row;

}

//retorna posicion x,y en posicion de icono para un indice dado
void menu_process_switcher_draw_icon_get_xy(zxvision_window *ventana,int indice_icono,int *pos_x,int *pos_y)
{

    int max_icons_per_row=menu_process_switcher_get_max_icons_per_row(ventana);


    *pos_x=indice_icono % max_icons_per_row;
    *pos_y=indice_icono / max_icons_per_row;
}

#define PROCESS_SWITCHER_MAXIMUM_ICONS 256

//Array de punteros a ventanas zxvision, donde se ordenaran por antiguedad
zxvision_window *menu_process_switcher_sorted_list[PROCESS_SWITCHER_MAXIMUM_ICONS];

int menu_process_switcher_total_icons=0;

zxvision_window *menu_process_switcher_get_window_n(int indice_buscar/*,zxvision_window *w*/)
{

    if (indice_buscar<0 || indice_buscar>=menu_process_switcher_total_icons) return NULL;

    else return menu_process_switcher_sorted_list[indice_buscar];

}

int menu_process_switcher_conmutar_ventana=0;

int menu_process_switcher_get_index_icon_on_mouse(zxvision_window *ventana)
{

    int cursor_mouse_x;
    int cursor_mouse_y;
    menu_process_switcher_calculate_mouse_xy_absolute_interface(&cursor_mouse_x,&cursor_mouse_y);

    int this_win_x=zxvision_window_get_pixel_x_position(ventana);
    int this_win_y=zxvision_window_get_pixel_y_position(ventana);

    //saltar la linea de titulo
    this_win_y +=menu_char_height*zoom_y*menu_gui_zoom;

    //printf("mouse on %d,%d\n",cursor_mouse_x,cursor_mouse_y);
    //printf("Window starts at %d,%d\n",this_win_x,this_win_y);

    int offset_in_window_x=cursor_mouse_x-this_win_x;
    int offset_in_window_y=cursor_mouse_y-this_win_y;

    offset_in_window_x /=zoom_x;
    offset_in_window_y /=zoom_y;

    offset_in_window_x /=menu_gui_zoom;
    offset_in_window_y /=menu_gui_zoom;

    //Obtener coordenada del icono pulsado dentro de la ventana
    if (offset_in_window_x>=0 && offset_in_window_y>=0) {
        offset_in_window_x /=PROCESS_SWITCHER_ICON_SEPARATION_X;
        offset_in_window_y /=PROCESS_SWITCHER_ICON_SEPARATION_Y;

        //printf("Pulsado en icono %d,%d\n",offset_in_window_x,offset_in_window_y);

        int max_icons_per_row=menu_process_switcher_get_max_icons_per_row(ventana);

        int indice_total_icono=offset_in_window_y*max_icons_per_row+offset_in_window_x;

        //printf("Pulsado en icono indice %d\n",indice_total_icono);

        return indice_total_icono;

    }

    return -1;
}


void menu_process_switcher_handle_click(zxvision_window *ventana)
{

    int indice_total_icono=menu_process_switcher_get_index_icon_on_mouse(ventana);
    if (indice_total_icono>=0) {


        zxvision_window *ventana_pulsada=menu_process_switcher_get_window_n(indice_total_icono/*,ventana*/);

        if (ventana_pulsada!=NULL) {
            //printf("Ventana pulsada: %s\n",ventana_pulsada->geometry_name);


            //TODO: esto funciona aunque no estoy del todo seguro que vaya a ir bien siempre...
            clicked_on_background_windows=1;
            which_window_clicked_on_background=ventana_pulsada;

            //Decir que hay que salir de aqui yendo a background, pero sin tener que dejar flag de background para la siguiente ventana
            menu_process_switcher_conmutar_ventana=1;

            //desminimizar ventana si estaba minimizada
            if (ventana_pulsada->is_minimized) {
                //printf("desminimizar ventana\n");
                zxvision_toggle_minimize_window(ventana_pulsada);
            }
        }
    }
}

void menu_process_switcher_handle_click_right(zxvision_window *ventana)
{

    int indice_total_icono=menu_process_switcher_get_index_icon_on_mouse(ventana);
    if (indice_total_icono>=0) {


        zxvision_window *ventana_pulsada=menu_process_switcher_get_window_n(indice_total_icono/*,ventana*/);

        if (ventana_pulsada!=NULL) {
            //printf("Ventana pulsada: %s\n",ventana_pulsada->geometry_name);



            //Quiero que el menu aparezca cerca del raton
            zxvision_set_next_menu_position_from_current_mouse();

            //Pero si process switcher esta siempre visible, apareceria por encima, desactivarlo temporalmente
            int antes_always_visible=ventana->always_visible;

            ventana->always_visible=0;

            //Esto muy parecido a process management pero no igual
            int respuesta=menu_simple_six_choices("Action","Do you want to",
                (ventana_pulsada->is_minimized ? "Unminimize" : "Minimize"),
                (ventana_pulsada->is_maximized ? "Unmaximize" : "Maximize"),
                "Switch always visible","Information","Close","Create link on ZX Desktop");

            ventana->always_visible=antes_always_visible;
            zxvision_reset_set_next_menu_position();


            switch (respuesta) {

                case 1:
                    //decimos que la ventana a minimizar no se dibujara como si fuera activa
                    zxvision_reset_ventana_tipo_activa();
                    zxvision_toggle_minimize_window(ventana_pulsada);
                    //despues del maximize, se ha redibujado esa ventana maximizada
                    //de alguna manera afectando a la ventana que es activa ahora
                    //hacer draw_window de nuestra ventana para corregir esto
                    //TODO: esto no se hace igual desde process management, al hacer maximize o minimize,
                    //no estoy seguro por que alli no hace falta

                    zxvision_set_ventana_tipo_activa();
                    zxvision_draw_window(ventana);
                break;

                case 2:
                    //decimos que la ventana a maximizar no se dibujara como si fuera activa
                    zxvision_reset_ventana_tipo_activa();
                    zxvision_toggle_maximize_window(ventana_pulsada);
                    //despues del maximize, se ha redibujado esa ventana maximizada
                    //de alguna manera afectando a la ventana que es activa ahora
                    //hacer draw_window de nuestra ventana para corregir esto

                    zxvision_set_ventana_tipo_activa();
                    zxvision_draw_window(ventana);
                break;

                case 3:
                    ventana_pulsada->always_visible ^=1;
                break;

                case 4:
                    menu_display_window_list_info(ventana_pulsada);
                break;


                case 5:
                    zxvision_window_delete_this_window(ventana_pulsada);
                break;

                case 6:
                    zxvision_create_link_desktop_from_window(ventana_pulsada);
                break;
            }

        }
    }
}

int menu_process_switcher_mouse_en_ventana(zxvision_window *w)
{
    if (zxvision_current_window==w && si_menu_mouse_en_ventana()) return 1;
    else return 0;
}

void menu_process_switcher_draw_recuadro(zxvision_window *ventana,int offset_x,int offset_y,int ancho_recuadro,int alto_recuadro,int color_recuadro)
{
    int x,y;

    for (x=0;x<ancho_recuadro;x++) {
        zxvision_putpixel(ventana,offset_x+x,offset_y,color_recuadro);
        zxvision_putpixel(ventana,offset_x+x,offset_y+alto_recuadro-1,color_recuadro);
    }
    for (y=0;y<alto_recuadro;y++) {
        zxvision_putpixel(ventana,offset_x,offset_y+y,color_recuadro);
        zxvision_putpixel(ventana,offset_x+ancho_recuadro-1,offset_y+y,color_recuadro);
    }
}

void menu_process_switcher_draw_icon(zxvision_window *ventana,char *geometry_name,int indice_icono,
    int seleccionado_indice_icono,int si_tarea_actual,int minimizada)
{

    char **puntero_bitmap;

    puntero_bitmap=zxvision_find_icon_for_known_window(geometry_name);

    if (puntero_bitmap==NULL) puntero_bitmap=bitmap_button_ext_desktop_userdefined;

   //int separacion_entre_iconos=ZESARUX_ASCII_LOGO_ANCHO*2;

    int offset_x;
    int offset_y;


    menu_process_switcher_draw_icon_get_xy(ventana,indice_icono,&offset_x,&offset_y);


    offset_x *=PROCESS_SWITCHER_ICON_SEPARATION_X;
    offset_y *=PROCESS_SWITCHER_ICON_SEPARATION_Y;

    //Primero poner todo el fondo del botón en color blanco
    int x,y;

    for (x=0;x<ZESARUX_ASCII_LOGO_ANCHO;x++) {
        for (y=0;y<ZESARUX_ASCII_LOGO_ALTO;y++) {
            zxvision_putpixel(ventana,offset_x+x,offset_y+y,7);
        }
    }



    //Y dibujar dicho botón
    int nivel_zoom=1;
    screen_put_asciibitmap_generic(puntero_bitmap,NULL,offset_x,offset_y,ZESARUX_ASCII_LOGO_ANCHO,ZESARUX_ASCII_LOGO_ALTO,
        0,menu_process_switcher_draw_icon_putpixel,nivel_zoom,0);

    //marca alrededor del seleccionado
    if (indice_icono==seleccionado_indice_icono) {
        //recuadro alrededor
        int alto_recuadro=ZESARUX_ASCII_LOGO_ALTO;
        int ancho_recuadro=ZESARUX_ASCII_LOGO_ANCHO;
        int color_recuadro=ESTILO_GUI_COLOR_AVISO;
        menu_process_switcher_draw_recuadro(ventana,offset_x,offset_y,ancho_recuadro,alto_recuadro,color_recuadro);
    }

    //marca alrededor del icono activo. como la teoria y la logica dice que no se puede estar seleccionando un icono
    //(logicamente esta es la ventana activa y esta como tal no tiene icono) y ademas ser esa la ventana activa, el recuadro
    //va a parar a la misma posicion y tamaño, porque los dos no se dan a la vez
    if (si_tarea_actual) {
        //recuadro alrededor
        int alto_recuadro=ZESARUX_ASCII_LOGO_ALTO;
        int ancho_recuadro=ZESARUX_ASCII_LOGO_ANCHO;
        int color_recuadro=ESTILO_GUI_PAPEL_SELECCIONADO;
        menu_process_switcher_draw_recuadro(ventana,offset_x,offset_y,ancho_recuadro,alto_recuadro,color_recuadro);
    }

    //Si esta minimizada, mismo puntito que sale en iconos de zx desktop con ventanas minimizadas
    if (minimizada) {
        //recuadro alrededor
        int x_puntito=offset_x+ZESARUX_ASCII_LOGO_ALTO-3;
        int y_puntito=offset_y+ZESARUX_ASCII_LOGO_ANCHO-3;
        int color_puntito=ESTILO_GUI_TINTA_NO_DISPONIBLE;

        int x,y;
        for (x=0;x<3;x++) {
            for (y=0;y<3;y++) {
                zxvision_putpixel(ventana,x_puntito+x,y_puntito+y,color_puntito);
            }
        }

    }


    //Y texto
    int max_texto=PROCESS_SWITCHER_ICON_SEPARATION_X/(CHARSET_ICONS_ANCHO+1);

    //quitar 1 caracter para que no quede pegado
    max_texto--;

    //Buscar el nombre corto descriptivo
    char *short_name;
    int id_ventana=zxvision_find_known_window(geometry_name);
    if (id_ventana>=0) short_name=zxvision_known_window_names_array[id_ventana].nombre_corto;
    else short_name=geometry_name;

    menu_process_switcher_draw_icon_text(offset_x,offset_y+ZESARUX_ASCII_LOGO_ALTO,short_name,max_texto);

}




int menu_process_switcher_get_total_tasks(zxvision_window *w)
{
    int total_tasks=0;

    if (zxvision_current_window!=NULL) {

        zxvision_window *pointer_window;

        pointer_window=zxvision_find_first_window_below_this(zxvision_current_window);

        //sacar una por una, las que permiten background

        while(pointer_window!=NULL) {
            //Si es una ventana que permite background y ademas no somos nosotros mismos
            if (pointer_window->can_be_backgrounded && pointer_window!=w) {

                //La metemos en el array que se va a ordenar despues
                menu_process_switcher_sorted_list[total_tasks]=pointer_window;

                total_tasks++;

            }

            pointer_window=pointer_window->next_window;
        }

    }

    return total_tasks;
}

int menu_process_switcher_sort_funct(const zxvision_window **d1, const zxvision_window **d2)
{

	//printf ("menu_filesel_alphasort %s %s\n",(*d1)->d_name,(*d2)->d_name );

	//compara pid
    //printf("Comparar %u con %u\n",d1->pid,d2->pid);
	return  ((*d1)->pid)  - ((*d2)->pid);
}



void menu_process_switcher_overlay(void)
{

    menu_speech_set_tecla_pulsada(); //Si no, envia continuamente todo ese texto a speech

    //si ventana minimizada, no ejecutar todo el codigo de overlay
    if (menu_process_switcher_window->is_minimized) return;

    zxvision_window *w=menu_process_switcher_window;

    //Print....
    //Tambien contar si se escribe siempre o se tiene en cuenta contador_segundo...


    int total_icons=menu_process_switcher_get_total_tasks(w);
    //printf("Total icons: %d\n",total_icons);

    if (total_icons>PROCESS_SWITCHER_MAXIMUM_ICONS) {
        //Maximum reached. Do nothing
        //printf("Maximum reached. Do nothing\n");
        menu_process_switcher_total_icons=0;
        return;
    }

    menu_process_switcher_total_icons=total_icons;

    //ordenar la lista de aplicaciones, para tenerla ordenada por "antigüedad", o sea, por el pid
    //en vez de la ordenacion que tiene de "desde abajo hasta arriba"
    //TODO: en caso que el contador de pid diera la vuelta (32 bits), cosa altamente improbable, a no ser
    //que se abrieran mas de 2^32 ventanas, quedarian ordenadas mal las ventanas posteriores
    if (menu_process_switcher_total_icons>0) {
        //printf("Before sort\n");
	    int (*funcion_compar)(const void *, const void *);

	    funcion_compar=( int (*)(const void *, const void *)  )menu_process_switcher_sort_funct;

	    qsort(menu_process_switcher_sorted_list,menu_process_switcher_total_icons,sizeof( zxvision_window *), funcion_compar);

        //printf("After sort\n");
    }




    int seleccionado_indice_icono=menu_process_switcher_get_index_icon_on_mouse(w);

    if (!menu_process_switcher_mouse_en_ventana(w)) {
        //printf("no en ventana\n");
        seleccionado_indice_icono=-1;
    }
    else {
        //printf("si en ventana\n");
    }

    int i;
    for (i=0;i<menu_process_switcher_total_icons;i++) {

        zxvision_window *pointer_window;
        pointer_window=menu_process_switcher_get_window_n(i);

        if (pointer_window!=NULL) {

            //printf("Pid %u window %s\n",pointer_window->pid,pointer_window->geometry_name);

            int si_tarea_actual=0;

            if (pointer_window==zxvision_current_window) si_tarea_actual=1;

            int minimizada=pointer_window->is_minimized;

            menu_process_switcher_draw_icon(w,pointer_window->geometry_name,i,seleccionado_indice_icono,si_tarea_actual,minimizada);
        }

    }



    //Mostrar colores
    zxvision_draw_window_contents(w);

}


void process_switcher_sync_immutable_setting(void)
{
    //Si la ventana esta ya abierta, ponemos o quitamos setting de immutable
    zxvision_window *ventana=zxvision_find_window_in_background("processswitcher");

    if (ventana!=NULL) {
        debug_printf(VERBOSE_DEBUG,"Found process switcher window, so syncing immutable setting");
        if (setting_process_switcher_immutable.v) ventana->not_altered_by_massive_changes=1;
        else ventana->not_altered_by_massive_changes=0;
    }
    //else printf("NO Encontrada ventana process switcher\n");
}

void process_switcher_sync_always_visible_setting(void)
{
    //Si la ventana esta ya abierta, ponemos o quitamos setting de immutable
    zxvision_window *ventana=zxvision_find_window_in_background("processswitcher");

    if (ventana!=NULL) {
        debug_printf(VERBOSE_DEBUG,"Found process switcher window, so syncing always visible setting");
        if (setting_process_switcher_always_visible.v) ventana->always_visible=1;
        else ventana->always_visible=0;
    }
    //else printf("NO Encontrada ventana process switcher\n");
}

void process_switcher_sync_always_left_bottom_setting(void)
{
    //Si la ventana esta ya abierta, ponemos o quitamos setting de immutable
    zxvision_window *ventana=zxvision_find_window_in_background("processswitcher");

    if (ventana!=NULL) {
        debug_printf(VERBOSE_DEBUG,"Found process switcher window, so syncing left-bottom setting");
        if (setting_process_switcher_force_left_bottom.v) {
            zxvision_set_x_position(ventana,0);
            zxvision_set_y_position(ventana,scr_get_menu_height()-ventana->visible_height);
            //ventana->x=0;
            //ventana->y=scr_get_menu_height()-ventana->visible_height-0;
        }
    }
    //else printf("NO Encontrada ventana process switcher\n");
}

//Almacenar la estructura de ventana aqui para que se pueda referenciar desde otros sitios
zxvision_window zxvision_window_process_switcher;


void menu_process_switcher(MENU_ITEM_PARAMETERS)
{
	menu_espera_no_tecla();

    if (!menu_multitarea) {
        menu_warn_message("This window needs multitask enabled");
        return;
    }

    zxvision_window *ventana;
    ventana=&zxvision_window_process_switcher;

	//IMPORTANTE! no crear ventana si ya existe. Esto hay que hacerlo en todas las ventanas que permiten background.
	//si no se hiciera, se crearia la misma ventana, y en la lista de ventanas activas , al redibujarse,
	//la primera ventana repetida apuntaria a la segunda, que es el mismo puntero, y redibujaria la misma, y se quedaria en bucle colgado
	//zxvision_delete_window_if_exists(ventana);

    //Crear ventana si no existe
    if (!zxvision_if_window_already_exists(ventana)) {
        int xventana,yventana,ancho_ventana,alto_ventana,is_minimized,is_maximized,ancho_antes_minimize,alto_antes_minimize;

        if (!util_find_window_geometry("processswitcher",&xventana,&yventana,&ancho_ventana,&alto_ventana,&is_minimized,&is_maximized,&ancho_antes_minimize,&alto_antes_minimize)) {
            ancho_ventana=43;
            alto_ventana=6;

            xventana=menu_center_x()-ancho_ventana/2;
            yventana=menu_center_y()-alto_ventana/2;
        }


        zxvision_new_window_gn_cim(ventana,xventana,yventana,ancho_ventana,alto_ventana,ancho_ventana-1,alto_ventana-2,"Process Switcher",
            "processswitcher",is_minimized,is_maximized,ancho_antes_minimize,alto_antes_minimize);

        ventana->can_be_backgrounded=1;

        //siempre visible. esto quiza controlar con un setting
        //TODO: de momento esto no
        //ventana->always_visible=1;

    }

    //Si ya existe, activar esta ventana
    else {
        zxvision_activate_this_window(ventana);
    }

    process_switcher_sync_immutable_setting();

    process_switcher_sync_always_visible_setting();

    process_switcher_sync_always_left_bottom_setting();

	zxvision_draw_window(ventana);

	z80_byte tecla;


	int salir=0;


    menu_process_switcher_window=ventana; //Decimos que el overlay lo hace sobre la ventana que tenemos aqui


    //cambio overlay
    zxvision_set_window_overlay(ventana,menu_process_switcher_overlay);

    //Por si acaso, si alguien pulsase en la ventana, antes de que se dibujase el primer overlay y cargada lista de ventanas,
    //si hubiera una lista anterior con algun puntero invalido, mejor decimos que de momento no hay iconos hasta que se ejecute el overlay
    menu_process_switcher_total_icons=0;


    //Toda ventana que este listada en zxvision_known_window_names_array debe permitir poder salir desde aqui
    //Se sale despues de haber inicializado overlay y de cualquier otra variable que necesite el overlay
    if (zxvision_currently_restoring_windows_on_start) {
            //printf ("Saliendo de ventana ya que la estamos restaurando en startup\n");
            return;
    }

    do {


		tecla=zxvision_common_getkey_refresh();


        switch (tecla) {

            //Salir con ESC
            case 2:
                salir=1;
            break;

            //O tecla background
            case 3:
                salir=1;
            break;

            case 0:

                if (mouse_left && menu_process_switcher_mouse_en_ventana(ventana) && !mouse_is_dragging) {
                    //printf("mouse_is_dragging: %d\n",mouse_is_dragging);
                    menu_process_switcher_handle_click(ventana);
                    if (menu_process_switcher_conmutar_ventana) {
                        salir=1;

                        //y tecla como background
                        tecla=3;
                    }
                }

                if (zxvision_pressed_right_mouse_button() && menu_process_switcher_mouse_en_ventana(ventana) && !mouse_is_dragging) {
                    //printf("mouse_is_dragging: %d\n",mouse_is_dragging);
                    menu_process_switcher_handle_click_right(ventana);
                }

            break;
        }


    } while (salir==0);


	util_add_window_geometry_compact(ventana);

	if (tecla==3) {
		zxvision_message_put_window_background();
	}

	else {

		zxvision_destroy_window(ventana);
	}

    menu_process_switcher_conmutar_ventana=0;

}



zxvision_window *menu_visual_cassette_tape_window;

//Ver si ha cambiado el porcentaje sobre los rollos, para redibujar
//eso indica que la cantidad de cinta enrollada es diferente
int menu_visual_cassette_tape_porcentaje_anterior=-1;

int antes_visual_cassette_tape_rodillo_arrastre_grados=-1;

int menu_visual_cassette_tape_temblor=0;








void menu_visual_cassette_tape_overlay(void)
{

    menu_speech_set_tecla_pulsada(); //Si no, envia continuamente todo ese texto a speech

    //si ventana minimizada, no ejecutar todo el codigo de overlay
    if (menu_visual_cassette_tape_window->is_minimized) return;



    if (realtape_inserted.v==0 && !is_tape_inserted() ) return;


    //Mostrar cinta, usar dibujo generico
    int dirty_por_standard_tape=0;

    int porcentaje;


    if (realtape_inserted.v) {
        //Para realtape
        int elapsed_seconds=realtape_get_elapsed_seconds();
        int total_seconds=realtape_get_total_seconds();

        //Tenemos que dar el porcentaje sobre el maximo, que es de una cinta de 90 (45 minutos por cara)
        //Sacar el porcentaje que representa el total de la cinta sobre una de 90
        //int porc_90=100;

        //Y aplicamos el transcurrido sobre ese valor

        if (total_seconds==0) porcentaje=0;
        else porcentaje=(elapsed_seconds*100)/total_seconds;

    }
    else {
        //Para cinta estándar
        long longitud_total=tape_get_file_size();
        long pos_actual=tape_block_ftell();
        if (longitud_total==0) porcentaje=0;
        else porcentaje=(pos_actual*100)/longitud_total;

        //printf("Total: %ld actual %ld porcentaje %d\n",longitud_total,pos_actual,porcentaje);

        if (porcentaje!=menu_visual_cassette_tape_porcentaje_anterior) {
            visual_cassette_tape_forzar_dibujado=1;

            //Temblor para que borre restos de cinta de porcentaje anterior
            menu_visual_cassette_tape_temblor^=1;

            //menu_visual_cassette_tape_window->dirty_user_must_draw_contents=1;
            //zxvision_cls(menu_visual_cassette_tape_window);

            //Para que borre restos de borrado de cinta sobre rodillo
            dirty_por_standard_tape=1;
        }

    }

    //Validacion porcentaje que no salga de los limites
    //Esto ha llegado a pasar insertando una cinta de formato no admitido (.zip), aunque no se
    //como ha podido insertarse pero sucedio haciendo pruebas
    if (porcentaje<0) porcentaje=0;
    if (porcentaje>100) porcentaje=100;


    int porcentaje_cinta_izquierdo=100-porcentaje;
    int porcentaje_cinta_derecho=porcentaje;

    int redibujar_rollos=0;

    if (porcentaje!=menu_visual_cassette_tape_porcentaje_anterior) redibujar_rollos=1;

    //determinar esto solo cuando sea necesario. probablemente con dirty
    int redibujar_parte_estatica=0;

    int redibujar_rodillos_arrastre=0;

    //Si se ha forzado redibujado y cinta parada, hacer temblar la cinta, esto hace
    //que no queden "rastros" de cinta tocando el rodillo, por ejemplo al avanzar cinta desde Visual Real Tape
    //Y hacemos temblar porque cuando reibuja los rodillos, borra la posicion anterior indicando temblor ^=1, y por tanto,
    //tenemos que ir conmutando aqui el temblor para que el borrado funcione bien
    if (realtape_inserted.v) {
        if (visual_cassette_tape_forzar_dibujado && realtape_playing.v==0) {
          menu_visual_cassette_tape_temblor^=1;
        }
    }


    //Si grados rodillos antes iguales a actual, no redibujar
    if (visual_cassette_tape_rodillo_arrastre_grados!=antes_visual_cassette_tape_rodillo_arrastre_grados) redibujar_rodillos_arrastre=1;

    //No redibujar si no hay cambios de nada
    if (menu_visual_cassette_tape_window->dirty_user_must_draw_contents || visual_cassette_tape_forzar_dibujado) {
        //printf("Redibujando parte estatica y dinamica y rodillos arrastre. %d\n",contador_segundo_infinito);
        redibujar_parte_estatica=1;
        redibujar_rollos=1;
        redibujar_rodillos_arrastre=1;


        menu_visual_cassette_tape_window->dirty_user_must_draw_contents=0;
    }

    visual_cassette_tape_forzar_dibujado=0;

    if (redibujar_rollos) {
        //printf("Redibujando parte dinamica. %d\n",contador_segundo_infinito);
    }


    int antes_porcentaje_cinta_izquierdo=100-menu_visual_cassette_tape_porcentaje_anterior;
    int antes_porcentaje_cinta_derecho=menu_visual_cassette_tape_porcentaje_anterior;

    //printf("%d\n",antes_porcentaje_cinta_derecho);

    int semaforos_hilow=0;


    //Si esta en movimiento, tiembla
    if (realtape_inserted.v) {
        if (realtape_playing.v) {
            menu_visual_cassette_tape_temblor^=1;
            redibujar_rodillos_arrastre=1;
        }
    }

    zxvision_window *w=menu_visual_cassette_tape_window;

    //Calcular tamaños


    int tamanyo_ocupado_viscastape_ancho=(w->visible_width-3)*menu_char_width;
    //quitamos 5: barra titulo,barra scroll, 2 lineas menu, 1 linea separacion
    int tamanyo_ocupado_viscastape_alto=(w->visible_height-5)*menu_char_height;

    int offset_x=menu_char_width*1;
    int offset_y=menu_char_height*3;

    //Ajustar escalas
    //Relacion de aspecto ideal: 1000 ancho, 630 alto


    int ancho_total_dibujo_virtual=GENERIC_VISUALTAPE_ANCHO_CINTA;


    int real_width=tamanyo_ocupado_viscastape_ancho;


    //Desactivar este trocito si queremos que el ancho pueda crecer independientemente del alto de ventana. SOLO PARA PRUEBAS
    int max_ancho_esperado_por_aspecto=(tamanyo_ocupado_viscastape_alto*ancho_total_dibujo_virtual)/GENERIC_VISUALTAPE_ALTO_CINTA;
    if (real_width>max_ancho_esperado_por_aspecto) {
        //Con esto el microdrive siempre esta dentro de la ventana entero, independientemente del tamaño de la ventana
        //printf("relacion ancho mal\n");
        real_width=max_ancho_esperado_por_aspecto;
    }


    int real_height=(real_width*GENERIC_VISUALTAPE_ALTO_CINTA)/ancho_total_dibujo_virtual;

    if (dirty_por_standard_tape) {
        menu_visual_cassette_tape_window->dirty_user_must_draw_contents=1;
    }

    menu_generic_visualtape(menu_visual_cassette_tape_window,
    real_width,real_height,offset_x,offset_y,
    porcentaje_cinta_izquierdo,porcentaje_cinta_derecho,
    antes_porcentaje_cinta_izquierdo,antes_porcentaje_cinta_derecho,
    visual_cassette_tape_rodillo_arrastre_grados,antes_visual_cassette_tape_rodillo_arrastre_grados,
    redibujar_rollos,redibujar_parte_estatica,redibujar_rodillos_arrastre,menu_visual_cassette_tape_temblor,semaforos_hilow,1);



    menu_visual_cassette_tape_porcentaje_anterior=porcentaje;
    antes_visual_cassette_tape_rodillo_arrastre_grados=visual_cassette_tape_rodillo_arrastre_grados;


    //Mostrar contenido
    zxvision_draw_window_contents(menu_visual_cassette_tape_window);

}



void menu_visual_cassette_tape_slow_movement(MENU_ITEM_PARAMETERS)
{
    visual_cassette_tape_slow_movement ^=1;
}

//Almacenar la estructura de ventana aqui para que se pueda referenciar desde otros sitios
zxvision_window zxvision_window_visual_cassette_tape;


void menu_visual_cassette_tape(MENU_ITEM_PARAMETERS)
{
	menu_espera_no_tecla();

    if (!menu_multitarea) {
        menu_warn_message("This window needs multitask enabled");
        return;
    }

    //forzar redibujar rollos. esto no hace falta, ya se hace con visual_cassette_tape_forzar_dibujado
    //menu_visual_cassette_tape_porcentaje_anterior=-1;

    zxvision_window *ventana;
    ventana=&zxvision_window_visual_cassette_tape;

	//IMPORTANTE! no crear ventana si ya existe. Esto hay que hacerlo en todas las ventanas que permiten background.
	//si no se hiciera, se crearia la misma ventana, y en la lista de ventanas activas , al redibujarse,
	//la primera ventana repetida apuntaria a la segunda, que es el mismo puntero, y redibujaria la misma, y se quedaria en bucle colgado
	//zxvision_delete_window_if_exists(ventana);

    //Crear ventana si no existe
    if (!zxvision_if_window_already_exists(ventana)) {
        int xventana,yventana,ancho_ventana,alto_ventana,is_minimized,is_maximized,ancho_antes_minimize,alto_antes_minimize;

        if (!util_find_window_geometry("visualcassettetape",&xventana,&yventana,&ancho_ventana,&alto_ventana,&is_minimized,&is_maximized,&ancho_antes_minimize,&alto_antes_minimize)) {
            ancho_ventana=43;
            alto_ventana=27;

            xventana=menu_center_x()-ancho_ventana/2;
            yventana=menu_center_y()-alto_ventana/2;
        }


        zxvision_new_window_gn_cim(ventana,xventana,yventana,ancho_ventana,alto_ventana,ancho_ventana-1,alto_ventana-2,"Visual Casette Tape",
            "visualcassettetape",is_minimized,is_maximized,ancho_antes_minimize,alto_antes_minimize);

        ventana->can_be_backgrounded=1;

        //definir color de papel de fondo
        ventana->default_paper=GENERIC_VISUALTAPE_COLOR_FONDO;
        zxvision_cls(ventana);
        //visual_microdrive_forzar_redraw=1;

    }

    //Si ya existe, activar esta ventana
    else {
        zxvision_activate_this_window(ventana);
    }

	zxvision_draw_window(ventana);

    //para mostrar correctamente el color del fondo alterado por default_paper
    zxvision_draw_window_contents(ventana);


    menu_visual_cassette_tape_window=ventana; //Decimos que el overlay lo hace sobre la ventana que tenemos aqui


    //cambio overlay
    zxvision_set_window_overlay(ventana,menu_visual_cassette_tape_overlay);


    //Toda ventana que este listada en zxvision_known_window_names_array debe permitir poder salir desde aqui
    //Se sale despues de haber inicializado overlay y de cualquier otra variable que necesite el overlay
    if (zxvision_currently_restoring_windows_on_start) {
            //printf ("Saliendo de ventana ya que la estamos restaurando en startup\n");
            return;
    }


	menu_item *array_menu_visual_cassette_tape;
	menu_item item_seleccionado;
	int retorno_menu;
	do {

        //Borrar posible texto anterior
        //zxvision_print_string_defaults_fillspc(ventana,1,1,"");

        visual_cassette_tape_forzar_dibujado=1;
        zxvision_cls(ventana);



        if (realtape_inserted.v==0 && !is_tape_inserted() ) zxvision_print_string_defaults_fillspc(ventana,1,1,"Tape is not inserted");

        zxvision_draw_window_contents(ventana);


		menu_add_item_menu_inicial_format(&array_menu_visual_cassette_tape,MENU_OPCION_NORMAL,menu_visual_cassette_tape_slow_movement,NULL
            ,"[%c] ~~Slow movement",(visual_cassette_tape_slow_movement ? 'X' : ' '));
		menu_add_item_menu_shortcut(array_menu_visual_cassette_tape,'s');
		menu_add_item_menu_ayuda(array_menu_visual_cassette_tape,"Slow movement");
		menu_add_item_menu_tabulado(array_menu_visual_cassette_tape,1,0);


		//Nombre de ventana solo aparece en el caso de stdout
		retorno_menu=menu_dibuja_menu_no_title_lang(&visualcasette_tape_opcion_seleccionada,&item_seleccionado,array_menu_visual_cassette_tape,"Visual Cassette Tape" );

		if (retorno_menu!=MENU_RETORNO_BACKGROUND) {
            //En caso de menus tabulados, es responsabilidad de este de borrar la ventana
            //Con este cls provoca que se borren todas las otras ventanas en background


            if ((item_seleccionado.tipo_opcion&MENU_OPCION_ESC)==0 && retorno_menu>=0) {
                    //llamamos por valor de funcion
                    if (item_seleccionado.menu_funcion!=NULL) {
                            //printf ("actuamos por funcion\n");
                            item_seleccionado.menu_funcion(item_seleccionado.valor_opcion);


                    }
            }
		}

	} while ( (item_seleccionado.tipo_opcion&MENU_OPCION_ESC)==0 && retorno_menu!=MENU_RETORNO_ESC && !salir_todos_menus && retorno_menu!=MENU_RETORNO_BACKGROUND);





	util_add_window_geometry_compact(ventana);

	if (retorno_menu==MENU_RETORNO_BACKGROUND) {
		zxvision_message_put_window_background();
	}

	else {

		zxvision_destroy_window(ventana);
	}


}



void menu_smartload(MENU_ITEM_PARAMETERS)
{
    //Si somos cliente de partida online como slave, no permitir esto
    if (zeng_online_connected.v && zeng_online_i_am_master.v==0) {
        menu_error_message("You can't Smartload while connected to ZENG online");
        //Evitar que se reabra menu principal, que genera duplicado el mismo menu,
        //dado que no se ha salido del menu con tecla cursor izquierda
        //Asi cerramos todos los menus
        salir_todos_menus=1;
        return;
    }

	menu_first_aid("smartload");

    char *filtros[]={
        "63", "80", "81", "ace","ay",   "bin",  "cas","cdt",
        "col","dck","dsk","epr","eprom","flash","m3u","nex","o",
        "p",  "p81", "pok","pzx","rom","rwa",  "rzx",  "scr",
        "sg", "smp","sms","sna","snx","sp",   "spg",  "tap",
        "trd", "mdr", "rmd", "tzx","wav","z80","z81","zmenu",
        "zsf","zx", 0
    };


        //guardamos directorio actual
        char directorio_actual[PATH_MAX];
        getcwd(directorio_actual,PATH_MAX);

        //Obtenemos directorio de snap
        //si no hay directorio, vamos a rutas predefinidas
        if (quickfile==NULL) menu_chdir_sharedfiles();
		else {
                char directorio[PATH_MAX];
                util_get_dir(quickfile,directorio);
                //printf ("strlen directorio: %d directorio: %s\n",strlen(directorio),directorio);

                //cambiamos a ese directorio, siempre que no sea nulo
                if (directorio[0]!=0) {
                        debug_printf (VERBOSE_INFO,"Changing to last directory: %s",directorio);
						//printf ("antes zvfs_chdir\n");
                        zvfs_chdir(directorio);
						//printf ("despues zvfs_chdir\n");
                }

				util_get_file_no_directory(quickfile,menu_filesel_posicionar_archivo_nombre);

				menu_filesel_posicionar_archivo.v=1;

        }






        int ret;

		//printf ("antes menu_filesel\n");

        ret=menu_filesel("Smart load",filtros,quickload_file);

		//printf ("despues menu_filesel\n");

        //volvemos a directorio inicial

        zvfs_chdir(directorio_actual);

        if (ret==1) {

			quickfile=quickload_file;

            //sin overlay de texto, que queremos ver las franjas de carga con el color normal (no apagado)
            //reset_menu_overlay_function();


            //Caso especial para los .pok file, se abre otra ventana
            if (!util_compare_file_extension(quickload_file,"pok")) {
                menu_debug_poke_pok_file_select(quickload_file);
            }

            else {


                if (quickload(quickload_file)) {
                    debug_printf (VERBOSE_ERR,"Unknown file format");
                }

            }

                //restauramos modo normal de texto de menu
                //set_menu_overlay_function(normal_overlay_texto_menu);

                //Y salimos de todos los menus si conviene
                if (no_close_menu_after_smartload.v==0) salir_todos_menus=1;
        }

	//printf ("tapefile: %p %s\n",tapefile,tapefile);

}





#define OSD_KEYBOARD_ANCHO_VENTANA 27
#define OSD_KEYBOARD_ALTO_VENTANA 12
#define OSD_KEYBOARD_X_VENTANA (menu_center_x()-OSD_KEYBOARD_ANCHO_VENTANA/2)
#define OSD_KEYBOARD_Y_VENTANA (menu_center_y()-OSD_KEYBOARD_ALTO_VENTANA/2)

struct s_osd_teclas {
    char tecla[5]; //4 de longitud mas 0 final
    int x; //posicion X en pantalla. Normalmente todos van separados dos caracteres excepto Symbol Shift, Enter etc que ocupan mas
    int ancho_tecla; //1 para todas, excepto enter, cap shift, etc
};



typedef struct s_osd_teclas osd_teclas;
/*

01234567890123456789012345

1 2 3 4 5 6 7 8 9 0
 Q W E R T Y U I O P
 A S D F G H J K L ENT
CS Z X C V B N M SS SP
*/

//Primeras 10 teclas son de la primera linea, siguientes 10, siguiente linea, etc
osd_teclas teclas_osd[40]={
    {"1",0,1}, {"2",2,1}, {"3",4,1}, {"4",6,1}, {"5",8,1}, {"6",10,1}, {"7",12,1}, {"8",14,1}, {"9",16,1}, {"0",18,1},
    {"Q",1,1}, {"W",3,1}, {"E",5,1}, {"R",7,1}, {"T",9,1}, {"Y",11,1}, {"U",13,1}, {"I",15,1}, {"O",17,1}, {"P",19,1},
    {"A",1,1}, {"S",3,1}, {"D",5,1}, {"F",7,1}, {"G",9,1}, {"H",11,1}, {"J",13,1}, {"K",15,1}, {"L",17,1}, {"ENT",19,3},
    {"CS",0,2}, {"Z",3,1}, {"X",5,1}, {"C",7,1}, {"V",9,1}, {"B",11,1}, {"N",13,1}, {"M",15,1}, {"SS",17,2}, {"SP",20,2}
};


int osd_keyboard_cursor_x=0; //Vale 0..9
int osd_keyboard_cursor_y=0; //Vale 0..3

int menu_onscreen_keyboard_return_index_cursor(void)
{
	return osd_keyboard_cursor_y*10+osd_keyboard_cursor_x;
}


//Comun para dibujar cursor y senyalar teclas activas
void menu_onscreen_keyboard_dibuja_cursor_aux(zxvision_window *ventana,char *s,int x,int y,int escursor)
{
	char *textocursor;
	char textospeech[32];
	textocursor=s;

        //Parchecillo para ZX80/81
        if (MACHINE_IS_ZX8081) {
		if (!strcmp(s,"SS")) textocursor=".";
		if (!strcmp(s,"ENT")) textocursor="NL";
        }


        //Si es teclas activas, texto inverso. Si es cursor, texto papel de color seleccion no disponible (rojo por defecto)
        if (escursor) {
		//menu_escribe_texto_ventana(x,y,ESTILO_GUI_TINTA_SELECCIONADO,ESTILO_GUI_PAPEL_SELECCIONADO,textocursor);
		//void zxvision_print_string(zxvision_window *w,int x,int y,int tinta,int papel,int parpadeo,char *texto)
		zxvision_print_string(ventana,x,y,ESTILO_GUI_TINTA_SELECCIONADO,ESTILO_GUI_PAPEL_SELECCIONADO,0,textocursor);
	}

	else {
		//menu_escribe_texto_ventana(x,y,ESTILO_GUI_TINTA_OPCION_MARCADA,ESTILO_GUI_PAPEL_OPCION_MARCADA,textocursor);
		zxvision_print_string(ventana,x,y,ESTILO_GUI_TINTA_OPCION_MARCADA,ESTILO_GUI_PAPEL_OPCION_MARCADA,0,textocursor);
	}



	//Enviar a speech
	string_a_minusculas(textocursor,textospeech);

	//Casos especiales para speech
	if (!strcmp(textospeech,".")) strcpy (textospeech,"dot");
	else if (!strcmp(textospeech,"cs")) strcpy (textospeech,"caps shift");
	else if (!strcmp(textospeech,"ss")) strcpy (textospeech,"symbol shift");
	else if (!strcmp(textospeech,"ent")) strcpy (textospeech,"enter");
	else if (!strcmp(textospeech,"sp")) strcpy (textospeech,"space");

	//Forzar que siempre suene en speech
	menu_speech_reset_tecla_pulsada();


	menu_textspeech_send_text(textospeech);
}

void menu_onscreen_keyboard_dibuja_cursor(zxvision_window *ventana)
{


	int offset_x=1;
	int offset_y=1;

	//Calcular posicion del cursor
	int x,y;
	y=osd_keyboard_cursor_y*2;
	//la x hay que buscarla en el array



	int indice=menu_onscreen_keyboard_return_index_cursor();

	//Si en stick
	if (osd_keyboard_cursor_y==4) {
		if (indice==40) menu_onscreen_keyboard_dibuja_cursor_aux(ventana,"Stick",offset_x,offset_y+y,1);
		else menu_onscreen_keyboard_dibuja_cursor_aux(ventana,"Send",offset_x+6,offset_y+y,1);
		return;
	}

	x=teclas_osd[indice].x;

	menu_onscreen_keyboard_dibuja_cursor_aux(ventana,teclas_osd[indice].tecla,offset_x+x,offset_y+y,1);





}


//Que teclas se estan pulsando. A 0 no tecla, a 1 tecla
z80_byte menu_osd_teclas_pulsadas[40];

int menu_onscreen_keyboard_sticky=0;


void menu_onscreen_keyboard_dibuja_teclas_activas(zxvision_window *ventana)
{
	int i,x,y;

	int offset_x=1;
	int offset_y=1;


	for (i=0;i<40;i++) {
		y=(i/10)*2;
		if (menu_osd_teclas_pulsadas[i]) {
			x=teclas_osd[i].x;
			menu_onscreen_keyboard_dibuja_cursor_aux(ventana,teclas_osd[i].tecla,offset_x+x,offset_y+y,0);
		}
	}

	if (menu_onscreen_keyboard_sticky) menu_onscreen_keyboard_dibuja_cursor_aux(ventana,"Stick",offset_x,offset_y+(40/10)*2,0);
}

void menu_onscreen_keyboard_reset_pressed_keys(void)
{
	int i;
	for (i=0;i<40;i++) menu_osd_teclas_pulsadas[i]=0;
}

void menu_osd_send_key_text(char *texto_tecla)
{
	//Enviar tecla ascii tal cual, excepto Enter, CS, SS, SP
		if (strlen(texto_tecla)==1) {
			z80_byte tecla=letra_minuscula(texto_tecla[0]);
			debug_printf (VERBOSE_DEBUG,"Sending Ascii key: %c",tecla);
			convert_numeros_letras_puerto_teclado_continue(tecla,1);
		}
		else {
/*
 A S D F G H J K L ENT
CS Z X C V B N M SS SP
*/
			if (!strcmp(texto_tecla,"ENT")) util_set_reset_key(UTIL_KEY_ENTER,1);

			//CS y SS los enviamos asi porque en algunos teclados, util_set_reset_key los gestiona diferente y no queremos

			else if (!strcmp(texto_tecla,"SS")) {
				if (MACHINE_IS_ZX8081) {
					puerto_32766 &=255-2;
				}
				else util_set_reset_key(UTIL_KEY_ALT_L,1);
			}


			else if (!strcmp(texto_tecla,"SP")) util_set_reset_key(UTIL_KEY_SPACE,1);


			//Si estan los flags de CS o SS, activarlos
			else if (!strcmp(texto_tecla,"CS")) util_set_reset_key(UTIL_KEY_SHIFT_L,1);
		//if (menu_button_osdkeyboard_caps.v) puerto_65278  &=255-1;
		//if (menu_button_osdkeyboard_symbol.v) puerto_32766 &=255-2;



		}
}

int menu_onscreen_send_enter_check_exit(z80_byte tecla)
{


	if (tecla==2) {
		//printf ("exit with ESC\n");

		return 1; //Salir con ESC
	}

	int indice;
	int salir=0;

	indice=menu_onscreen_keyboard_return_index_cursor();
	if (indice==40) {
        //En sticky
        menu_onscreen_keyboard_sticky ^=1;

        if (!menu_onscreen_keyboard_sticky) menu_onscreen_keyboard_reset_pressed_keys();
	}

	else {


		//Si esta en modo sticky, agregar tecla a la lista y enviar todas
		//Si no, solo enviar una tecla
		if (!menu_onscreen_keyboard_sticky) {
			menu_onscreen_keyboard_reset_pressed_keys();
		}

		//Agregar esa tecla al array, siempre que no sea orden "send"
	        indice=menu_onscreen_keyboard_return_index_cursor();

	        if (indice!=41) {
			menu_osd_teclas_pulsadas[indice] ^=1;
			if (menu_osd_teclas_pulsadas[indice]) debug_printf (VERBOSE_DEBUG,"Adding key %s",teclas_osd[indice].tecla);
			else debug_printf (VERBOSE_DEBUG,"Clearing key %s",teclas_osd[indice].tecla);
		}

		//Si es modo stick, solo enviar cuando pulsar "Send"
		//Si no modo stick, enviar la que haya
		//int enviar=1;
		salir=1;
		if (menu_onscreen_keyboard_sticky && indice!=41) {
			salir=0;
			//enviar=0;
		}

	}


	return salir;

}

void menu_onscreen_keyboard(MENU_ITEM_PARAMETERS)
{
	//Si maquina no es Spectrum o zx80/81, volver
	if (!MACHINE_IS_SPECTRUM && !MACHINE_IS_ZX8081) return;


	//Evitar que se pueda llamar al mismo osd desde aqui dentro
	int antes_osd_kb_no_mostrar_desde_menu=osd_kb_no_mostrar_desde_menu;
	osd_kb_no_mostrar_desde_menu=1;


	if (!menu_onscreen_keyboard_sticky) menu_onscreen_keyboard_reset_pressed_keys();


	zxvision_window ventana;


    int ancho_ventana;
    int alto_ventana;
    int xventana;
    int yventana;

    int is_minimized,is_maximized,ancho_antes_minimize,alto_antes_minimize;

    if (!util_find_window_geometry("osdkeyboard",&xventana,&yventana,&ancho_ventana,&alto_ventana,&is_minimized,&is_maximized,&ancho_antes_minimize,&alto_antes_minimize)) {
        ancho_ventana=OSD_KEYBOARD_ANCHO_VENTANA;
        alto_ventana=OSD_KEYBOARD_ALTO_VENTANA;
        xventana=OSD_KEYBOARD_X_VENTANA;
        yventana=OSD_KEYBOARD_Y_VENTANA;
    }


    zxvision_new_window(&ventana,xventana,yventana,ancho_ventana,alto_ventana,
                        ancho_ventana-1,alto_ventana-2,"On Screen Keyboard");

    strcpy(ventana.geometry_name,"osdkeyboard");

    zxvision_draw_window(&ventana);

	z80_byte tecla;

	int salir=0;



	do {

        int linea=1;

            //01234567890123456789012345678901
        char textoventana[32];

		int fila_tecla;
		int pos_tecla_en_fila;

		int indice_tecla=0;

		for (fila_tecla=0;fila_tecla<4;fila_tecla++) {
		//Inicializar texto linea con 31 espacios
					                  //1234567890123456789012345678901
			sprintf (textoventana,"%s","                               ");
			for (pos_tecla_en_fila=0;pos_tecla_en_fila<10;pos_tecla_en_fila++) {
				//Copiar texto tecla a buffer linea
				int len=teclas_osd[indice_tecla].ancho_tecla;
				int tecla_x=teclas_osd[indice_tecla].x;
				int i;

				//parchecillo para ZX80/81. Si es Symbol Shift, Realmente es el .
				if (MACHINE_IS_ZX8081 && !strcmp(teclas_osd[indice_tecla].tecla,"SS")) {
					textoventana[tecla_x+0]='.';
					textoventana[tecla_x+1]=' ';  //Como ancho de tecla realmente es dos, meter un espacio
				}

				//parchecillo para ZX80/81. Si es ENT, Realmente es NL
				else if (MACHINE_IS_ZX8081 && !strcmp(teclas_osd[indice_tecla].tecla,"ENT")) {
					textoventana[tecla_x+0]='N';
					textoventana[tecla_x+1]='L';
					textoventana[tecla_x+2]=' ';  //Como ancho de tecla realmente es tres, meter un espacio
				}

				else for (i=0;i<len;i++) {
					textoventana[tecla_x+i+0]=teclas_osd[indice_tecla].tecla[i];
				}

				indice_tecla++;
			}

			//Meter final de cadena
			textoventana[OSD_KEYBOARD_ANCHO_VENTANA-2]=0;


			//No queremos que se envie cada linea a speech
			z80_bit old_textspeech_also_send_menu;
			old_textspeech_also_send_menu.v=textspeech_also_send_menu.v;
			textspeech_also_send_menu.v=0;


			zxvision_print_string_defaults_fillspc(&ventana,1,linea++,textoventana);


			//Restaurar parametro speech
			textspeech_also_send_menu.v=old_textspeech_also_send_menu.v;

			linea++;
		}

		//No quiero que envie ni "Stick" ni "Send" a speech
		z80_bit old_textspeech_also_send_menu;
		old_textspeech_also_send_menu.v=textspeech_also_send_menu.v;
		textspeech_also_send_menu.v=0;


		zxvision_print_string_defaults_fillspc(&ventana,1,linea++,"Stick Send");

		//Restaurar parametro speech
		textspeech_also_send_menu.v=old_textspeech_also_send_menu.v;


		menu_onscreen_keyboard_dibuja_teclas_activas(&ventana);

		menu_onscreen_keyboard_dibuja_cursor(&ventana);


		zxvision_draw_window_contents(&ventana);



		tecla=zxvision_common_getkey_refresh();

        //Si movido raton
        if (tecla==0) {
            if (si_menu_mouse_activado() && si_menu_mouse_en_ventana() ) {
                //printf("despues\n");
                //printf("mouse_x %d mouse_y %d\n",menu_mouse_x,menu_mouse_y);
                int posicion_mouse_x=menu_mouse_x-1;
                int posicion_mouse_y=menu_mouse_y-2;

                int mouse_selecciona_tecla=0;

                //Si raton en ventana

                if (posicion_mouse_y<8) {
                    int i;
                    for (i=0;i<40 && !mouse_selecciona_tecla;i++) {
                        int y=(i/10)*2;
                        int xtecla=teclas_osd[i].x;
                        int anchotecla=teclas_osd[i].ancho_tecla;
                        if (posicion_mouse_x>=xtecla && posicion_mouse_x<xtecla+anchotecla &&
                            y==posicion_mouse_y) {

                            //printf("mouse sobre tecla: %s\n",teclas_osd[i].tecla);
                            osd_keyboard_cursor_y=i / 10;
                            osd_keyboard_cursor_x=i % 10;
                            mouse_selecciona_tecla=1;
                        }
                    }
                }

                else if (posicion_mouse_y==8) {
                    mouse_selecciona_tecla=1;
                    //posicion en sticky o send
                    osd_keyboard_cursor_x=0;
                    osd_keyboard_cursor_y=4;

                    if (posicion_mouse_x>=6) osd_keyboard_cursor_x++;
                }


                //Enviar enter si se pulsa el raton, pero solo si se hace click en tecla valida
                if (mouse_left && mouse_selecciona_tecla) {
                    //printf("PULSADA ENTER\n");
                    tecla=13;
                    menu_espera_no_tecla();
                }

            }
        }

		//tambien permitir mover con joystick aunque no este mapeado a cursor key
		switch (tecla) {
			case 11:
				//arriba
				if (osd_keyboard_cursor_y>0) osd_keyboard_cursor_y--;
			break;

			case 10:
				//abajo
				if (osd_keyboard_cursor_y<4) osd_keyboard_cursor_y++;
				if (osd_keyboard_cursor_y==4) osd_keyboard_cursor_x=0;  //Cursor en sticky
			break;

			case 8:
				//izquierda
				if (osd_keyboard_cursor_x>0) osd_keyboard_cursor_x--;
			break;

			case 9:
				//derecha
				if (osd_keyboard_cursor_y==4) {  //Si en linea inferior
					if (osd_keyboard_cursor_x<1) osd_keyboard_cursor_x++;
				}
				else if (osd_keyboard_cursor_x<9) osd_keyboard_cursor_x++;
			break;

			case 2: //ESC
			case 13: //Enter
				salir=menu_onscreen_send_enter_check_exit(tecla);

			break;
		}

        //Si pulsado letras o numeros
        if (
            (tecla>='a' && tecla<='z') ||
            (tecla>='0' && tecla<='9') ||
            (tecla==' ')
        ) {
            //printf("Pulsada tecla %c\n",tecla);
            char buffer_tecla[10];
            buffer_tecla[0]=tecla;
            buffer_tecla[1]=0;

            if (tecla==' ') strcpy(buffer_tecla,"SP");

            int i;
            for (i=0;i<40;i++) {

                if (!strcasecmp(buffer_tecla,teclas_osd[i].tecla)) {
                    //printf("pulsada sobre tecla: %s\n",teclas_osd[i].tecla);
                    osd_keyboard_cursor_y=i / 10;
                    osd_keyboard_cursor_x=i % 10;
                    tecla=13;
                    salir=menu_onscreen_send_enter_check_exit(tecla);
                }

            }
        }

	} while (salir==0);

	menu_espera_no_tecla();

	//Si salido con Enter o Fire joystick
	if (tecla==13) {

        //Liberar otras teclas, por si acaso
        reset_keyboard_ports();
        int i;
        for (i=0;i<40;i++) {
            if (menu_osd_teclas_pulsadas[i]) {
                //printf ("Sending key %s\n",teclas_osd[i].tecla);
                menu_osd_send_key_text(teclas_osd[i].tecla);
            }
        }


        //printf ("Exiting and sending\n");

        salir_todos_menus=1;
        timer_on_screen_key=25; //durante medio segundo


	}



    //Grabar geometria ventana
    util_add_window_geometry_compact(&ventana);

    zxvision_destroy_window(&ventana);

	//Si no se ha salido con escape, hacer que vuelva y quitar pulsaciones de caps y symbol
	if (tecla!=2) {
		menu_button_osdkeyboard_return.v=1;
	}
	else {
		//se sale con esc, quitar pulsaciones de caps y symbol
		menu_onscreen_keyboard_reset_pressed_keys();
		menu_onscreen_keyboard_sticky=0;

	}

	osd_kb_no_mostrar_desde_menu=antes_osd_kb_no_mostrar_desde_menu;

}

char menu_exit_emulator_additional_save_config[100]="";
char menu_exit_emulator_additional_save_snapshot[100]="";

z80_bit save_snapshot_file_on_exit_dialog={1};

char *menu_exit_emulator_additional_item(void)
{
    sprintf(menu_exit_emulator_additional_save_config,
        "[%c] Save config",(save_configuration_file_on_exit.v ? 'X' : ' '));

    return menu_exit_emulator_additional_save_config;
}


void menu_exit_emulator_additional_item_trigger(void)
{
    save_configuration_file_on_exit.v ^=1;
}


char *menu_exit_emulator_additional_item2(void)
{
    sprintf(menu_exit_emulator_additional_save_snapshot,
        "[%c] Save snapshot",(save_snapshot_file_on_exit_dialog.v ? 'X' : ' '));

    return menu_exit_emulator_additional_save_snapshot;
}


void menu_exit_emulator_additional_item_trigger2(void)
{
    save_snapshot_file_on_exit_dialog.v ^=1;
}

void menu_exit_emulator(MENU_ITEM_PARAMETERS)
{

	menu_reset_counters_tecla_repeticion();

    //Conservar el estado anterior por si el usuario finalmente no sale
    int antes_save_configuration_file_on_exit=save_configuration_file_on_exit.v;

	int salir=0;

	//Si quickexit, no preguntar
	if (quickexit.v) salir=1;

	else {
        //Si tiene opcion de guardado configuracion, damos opcion para que no la guarde
        if (save_configuration_file_on_exit.v) {
            if (autosave_snapshot_on_exit.v) {
                //Permitimos no grabar config ni no grabar snapshot
                salir=menu_confirm_yesno_texto_additional_item("Exit ZEsarUX",menu_get_string_language("Sure?"),
                    menu_exit_emulator_additional_item,menu_exit_emulator_additional_item_trigger,
                    menu_exit_emulator_additional_item2,menu_exit_emulator_additional_item_trigger2
                    );
            }

            else {
                //Permitimos no grabar config
                salir=menu_confirm_yesno_texto_additional_item("Exit ZEsarUX",menu_get_string_language("Sure?"),
                    menu_exit_emulator_additional_item,menu_exit_emulator_additional_item_trigger,
                    NULL,NULL
                    );
            }
        }
        else {
            salir=menu_confirm_yesno("Exit ZEsarUX");
        }
    }

    if (salir) {


        cls_menu_overlay();

        reset_menu_overlay_function();
        menu_abierto=0;


        end_emulator_autosave_snapshot();

    }

    else {
        //restaurar estado anterior porque el usuario no ha salido
        save_configuration_file_on_exit.v=antes_save_configuration_file_on_exit;
    }

    cls_menu_overlay();
}




void menu_principal_salir_emulador(MENU_ITEM_PARAMETERS)
{
	menu_exit_emulator(0);
}

char menu_search_campo_busqueda[MAX_TEXTO_OPCION]="";

void menu_search(MENU_ITEM_PARAMETERS)
{
    menu_ventana_scanf("Query",menu_search_campo_busqueda,MAX_TEXTO_OPCION);

    if (menu_search_campo_busqueda[0]==0) {
        menu_error_message("Write at least one character");
        return;
    }

    char *text_buffer=util_malloc_max_texto_generic_message("Can not allocate memory for search");
    //Empezar con cadena vacia para poder concatenar
    text_buffer[0]=0;

    //Buscar en todos los indices

    index_menu *menu=first_index_menu;

    while (menu!=NULL) {
        //Y ahora buscar en cada linea de menu
        index_menu_linea *linea_menu=menu->first_item_menu;

        while (linea_menu!=NULL) {
            char *encontrado=util_strcasestr(linea_menu->texto_opcion,menu_search_campo_busqueda);
            if (encontrado!=NULL) {
                //printf("Encontrado texto [%s] en linea [%s] de menu [%s]\n",
                //    menu_search_campo_busqueda,linea_menu->texto_opcion,menu->titulo_menu);

                //Tiene que caber nombre menu (con ->) y nombre linea
                char buf_linea[MAX_TEXTO_OPCION*10+MAX_TEXTO_OPCION+30];

                sprintf(buf_linea,"{%s} on menu:\n{%s}\n\n",linea_menu->texto_opcion,menu->titulo_menu);

                util_concat_string(text_buffer,buf_linea,MAX_TEXTO_GENERIC_MESSAGE);
            }

            linea_menu=linea_menu->next_item_menu;
        }

        menu=menu->next_menu;
    }

    if (text_buffer[0]==0) strcpy(text_buffer,"(Nothing)");

    menu_generic_message("Search results",text_buffer);

    free(text_buffer);
}

int force_menu_dibuja_menu_recorrer_menus=0;

void menu_switch_simple_menus(MENU_ITEM_PARAMETERS)
{
    //La vista cambia pero el setting menu_show_simple_items_by_config.v se mantiene, por tanto
    //si se ha entrado con vista simple, aunque el usuario conmute a vista completa, se guardara la configuración al salir como vista simple
    menu_show_simple_items.v ^=1;
}

int menu_inicio_mostrar_main_menu(int salir_menu)
{

    if (force_menu_dibuja_menu_recorrer_menus) {
        force_menu_dibuja_menu_recorrer_menus=0;
        menu_dibuja_menu_recorrer_menus=1;
        menu_dibuja_menu_recorrer_menus_entrado_submenu=1;
    }

            int retorno_menu;

            menu_item *array_menu_principal;
            menu_item item_seleccionado;

            menu_add_item_menu_en_es_ca_inicial(&array_menu_principal,MENU_OPCION_NORMAL,menu_smartload,NULL,
                "~~Smart load","Carga ingenio~~sa","Càrrega enginyo~~sa");
            menu_add_item_menu_shortcut(array_menu_principal,'s');
            //El menu smartload hara cerrar todas las ventanas o no dependiendo de setting no_close_menu_after_smartload
            //menu_add_item_menu_se_cerrara(array_menu_principal);
            menu_add_item_menu_genera_ventana(array_menu_principal);
            menu_add_item_menu_es_sencillo(array_menu_principal);
            menu_add_item_menu_tooltip(array_menu_principal,"Smart load tapes, snapshots, floppies, memory cards, cartridges...");
            menu_add_item_menu_ayuda(array_menu_principal,"This option loads the file depending on its type: \n"
                "-Binary tapes are inserted as standard tapes and loaded quickly\n"
                "-Audio tapes are loaded as real tapes\n"
                "-Snapshots are loaded at once\n"
                "-Cartridges or memory cards are inserted on the machine\n"
                "Note: Tapes will be autoloaded if the autoload setting is on (by default)"

                );


            menu_add_item_menu_en_es_ca(array_menu_principal,MENU_OPCION_NORMAL,menu_snapshot,NULL,
                "S~~napshot","I~~nstantánea","I~~nstantània");
            menu_add_item_menu_shortcut(array_menu_principal,'n');
            menu_add_item_menu_tooltip(array_menu_principal,"Load or save snapshots");
            menu_add_item_menu_ayuda(array_menu_principal,"Load or save different snapshot images. Snapshot images are loaded or saved at once");
            menu_add_item_menu_tiene_submenu(array_menu_principal);


            menu_add_item_menu_en_es_ca(array_menu_principal,MENU_OPCION_NORMAL,menu_machine_selection,NULL,
                "~~Machine","~~Máquina","~~Màquina");
            menu_add_item_menu_shortcut(array_menu_principal,'m');
            menu_add_item_menu_es_sencillo(array_menu_principal);
            menu_add_item_menu_tooltip(array_menu_principal,"Change active machine");
            menu_add_item_menu_ayuda(array_menu_principal,"You can switch to another machine. It also resets the machine");
            menu_add_item_menu_tiene_submenu(array_menu_principal);

            //Este item solo sale en modo sencillo
            if (menu_show_simple_items.v) {
                menu_add_item_menu(array_menu_principal,"~~Poke",MENU_OPCION_NORMAL,menu_poke,NULL);
                menu_add_item_menu_shortcut(array_menu_principal,'p');
                menu_add_item_menu_es_sencillo(array_menu_principal);
                menu_add_item_menu_tooltip(array_menu_principal,"Poke address manually or from .POK file");
                menu_add_item_menu_ayuda(array_menu_principal,"Poke address for infinite lives, etc...");
                menu_add_item_menu_tiene_submenu(array_menu_principal);
            }


            menu_add_item_menu(array_menu_principal,"~~Audio",MENU_OPCION_NORMAL,menu_audio,NULL);
            menu_add_item_menu_shortcut(array_menu_principal,'a');
            menu_add_item_menu_tooltip(array_menu_principal,"Audio related actions");
            menu_add_item_menu_ayuda(array_menu_principal,"Audio related actions");
            menu_add_item_menu_tiene_submenu(array_menu_principal);


            menu_add_item_menu(array_menu_principal,"~~Display",MENU_OPCION_NORMAL,menu_display_settings,NULL);
            menu_add_item_menu_shortcut(array_menu_principal,'d');
            menu_add_item_menu_tooltip(array_menu_principal,"Display related actions");
            menu_add_item_menu_ayuda(array_menu_principal,"Display related actions");
            menu_add_item_menu_tiene_submenu(array_menu_principal);


            menu_add_item_menu_en_es_ca(array_menu_principal,MENU_OPCION_NORMAL,menu_storage,NULL,
                "S~~torage","Almacenamien~~to","Emmagatzema~~tge");
            menu_add_item_menu_shortcut(array_menu_principal,'t');
            menu_add_item_menu_tooltip(array_menu_principal,"Select storage mediums, like tape, MMC, IDE, etc");
            menu_add_item_menu_ayuda(array_menu_principal,"Select storage mediums, like tape, MMC, IDE, etc");
            menu_add_item_menu_tiene_submenu(array_menu_principal);


            menu_add_item_menu(array_menu_principal,"D~~ebug",MENU_OPCION_NORMAL,menu_debug_main,NULL);
            menu_add_item_menu_shortcut(array_menu_principal,'e');
            menu_add_item_menu_tooltip(array_menu_principal,"Debug tools");
            menu_add_item_menu_ayuda(array_menu_principal,"Tools to debug the machine");
            menu_add_item_menu_tiene_submenu(array_menu_principal);


            menu_add_item_menu(array_menu_principal,"Netw~~ork",MENU_OPCION_NORMAL,menu_network,NULL);
            menu_add_item_menu_shortcut(array_menu_principal,'o');
            menu_add_item_menu_tooltip(array_menu_principal,"Network related actions");
            menu_add_item_menu_ayuda(array_menu_principal,"Network related actions");
            menu_add_item_menu_tiene_submenu(array_menu_principal);


            if (menu_allow_background_windows) {
                menu_add_item_menu(array_menu_principal,"~~Windows",MENU_OPCION_NORMAL,menu_windows,NULL);
                menu_add_item_menu_shortcut(array_menu_principal,'w');
                menu_add_item_menu_tooltip(array_menu_principal,"Window management");
                menu_add_item_menu_ayuda(array_menu_principal,"Window management");
                menu_add_item_menu_tiene_submenu(array_menu_principal);
            }


            menu_add_item_menu(array_menu_principal,"",MENU_OPCION_SEPARADOR,NULL,NULL);


            menu_add_item_menu_en_es_ca(array_menu_principal,MENU_OPCION_NORMAL,menu_settings,NULL,
                "Sett~~ings","Opc~~iones","Opc~~ions");
            menu_add_item_menu_shortcut(array_menu_principal,'i');
            menu_add_item_menu_tooltip(array_menu_principal,"General Settings");
            menu_add_item_menu_ayuda(array_menu_principal,"General Settings");
            menu_add_item_menu_tiene_submenu(array_menu_principal);

            //Poder conmutar de modo sencillo a normal pero solo cuando se ha arrancado desde modo sencillo
            if (menu_show_simple_items_by_config.v) {
                if (menu_show_simple_items.v) {
                    menu_add_item_menu(array_menu_principal,"Show full menus",MENU_OPCION_NORMAL,menu_switch_simple_menus,NULL);
                    menu_add_item_menu_es_sencillo(array_menu_principal);
                }
                else {
                    menu_add_item_menu(array_menu_principal,"Show simple menus",MENU_OPCION_NORMAL,menu_switch_simple_menus,NULL);
                    menu_add_item_menu_es_sencillo(array_menu_principal);
                }
            }

            if (index_menu_enabled.v) {
                menu_add_item_menu_en_es_ca(array_menu_principal,MENU_OPCION_NORMAL,menu_search,NULL,
                    "Sea~~rch menu","Busca~~r menú","Busca~~r menú");
                menu_add_item_menu_shortcut(array_menu_principal,'r');
                menu_add_item_menu_tooltip(array_menu_principal,"Search menu");
                menu_add_item_menu_ayuda(array_menu_principal,"Search menu");
                menu_add_item_menu_genera_ventana(array_menu_principal);
                menu_add_item_menu_se_cerrara(array_menu_principal);
            }

            menu_add_item_menu_en_es_ca(array_menu_principal,MENU_OPCION_NORMAL,menu_toys,NULL,
                "~~Procrastinate","~~Procrastinar","~~Procrastinar");
            menu_add_item_menu_shortcut(array_menu_principal,'p');
            menu_add_item_menu_tooltip(array_menu_principal,"Some toys to procrastinate real work");
            menu_add_item_menu_ayuda(array_menu_principal,"Some toys to procrastinate real work");
            menu_add_item_menu_tiene_submenu(array_menu_principal);


            menu_add_item_menu(array_menu_principal,"He~~lp",MENU_OPCION_NORMAL,menu_help,NULL);
            menu_add_item_menu_shortcut(array_menu_principal,'l');
            menu_add_item_menu_tooltip(array_menu_principal,"Help menu");
            menu_add_item_menu_ayuda(array_menu_principal,"Some help and related files");
            menu_add_item_menu_tiene_submenu(array_menu_principal);


            menu_add_item_menu_separator(array_menu_principal);

            menu_add_item_menu_en_es_ca(array_menu_principal,MENU_OPCION_NORMAL|MENU_OPCION_ESC,NULL,NULL,
                "Close menu","Cerrar menu","Tancar menu");
            menu_add_item_menu_prefijo_format(array_menu_principal,"%s ",esc_key_message);
            menu_add_item_menu_tooltip(array_menu_principal,string_esc_closes_menus);
            menu_add_item_menu_ayuda(array_menu_principal,string_esc_closes_menus);

            menu_add_item_menu_en_es_ca(array_menu_principal,MENU_OPCION_NORMAL,menu_principal_salir_emulador,NULL,
                "Exit ZEsarUX","Salir de ZEsarUX","Sortir de ZEsarUX");
            menu_add_item_menu_prefijo_format(array_menu_principal,"%s",(f_functions==1 ? "F10 ": "") );
            menu_add_item_menu_es_sencillo(array_menu_principal);
            menu_add_item_menu_tooltip(array_menu_principal,"Exit ZEsarUX");
            menu_add_item_menu_ayuda(array_menu_principal,"Exit ZEsarUX");


            //La llamada a menu_add_item_menu_index_full_path lo agrega al principio del array, por tanto lo podemos llamar desde cualquier
            //punto de la creacion del menu. Lo llamo desde aqui porque queda esteticamente mas bonito
            menu_add_item_menu_index_full_path(array_menu_principal,"Main Menu","Menú Principal","Menú Principal");

            //retorno_menu=menu_dibuja_menu_no_title_lang(&menu_inicio_opcion_seleccionada,&item_seleccionado,array_menu_principal,"ZEsarUX v." EMULATOR_VERSION );
            retorno_menu=menu_dibuja_menu(&menu_inicio_opcion_seleccionada,&item_seleccionado,array_menu_principal,
                "Main Menu","Menú Principal","Menú Principal" );

            //printf ("Opcion seleccionada: %d\n",menu_inicio_opcion_seleccionada);
            //printf ("Tipo opcion: %d\n",item_seleccionado.tipo_opcion);
            //printf ("Retorno menu: %d\n",retorno_menu);


            if ( (retorno_menu!=MENU_RETORNO_ESC) &&  (retorno_menu==MENU_RETORNO_F10)  ) {

                //menu_exit_emulator(0);
                menu_principal_salir_emulador(0);

            }


            else if (retorno_menu>=0) {
                //llamamos por valor de funcion
                if (item_seleccionado.menu_funcion!=NULL) {
                    //printf ("actuamos por funcion\n");

                    item_seleccionado.menu_funcion(item_seleccionado.valor_opcion);


                    //si ha generado error, no salir
                    if (if_pending_error_message) salir_todos_menus=0;
                }


            }



            if (retorno_menu==MENU_RETORNO_ESC || (item_seleccionado.tipo_opcion & MENU_OPCION_ESC) == MENU_OPCION_ESC) {
                //printf ("opcion ESC o pulsado ESC\n");
                salir_menu=1;
            }

    return salir_menu;

}

void menu_inicio_bucle_main(void)
{
    //printf("menu_inicio_bucle_main: menu_pressed_zxdesktop_button_which %d menu_pressed_zxdesktop_lower_icon_which %d pulsado_alguna_ventana_con_menu_cerrado %d\n",
    //            menu_pressed_zxdesktop_button_which,menu_pressed_zxdesktop_lower_icon_which,pulsado_alguna_ventana_con_menu_cerrado);

    //printf("menu_inicio_bucle_main pulsado_alguna_ventana_con_menu_cerrado: %d\n",pulsado_alguna_ventana_con_menu_cerrado);
    //Primera ayuda siempre que no se haya pulsado en botones de menu diferentes del menu principal o dispositivos
    int mostrar_first_aid_menu=1;
    if (menu_pressed_zxdesktop_lower_icon_which>=0) mostrar_first_aid_menu=0;
    if (menu_pressed_zxdesktop_configurable_icon_which>=0) mostrar_first_aid_menu=0;
    if (menu_pressed_zxdesktop_right_button_background>=0) mostrar_first_aid_menu=0;
    if (pulsado_alguna_ventana_con_menu_cerrado) mostrar_first_aid_menu=0;

    //boton de menu distinto del boton de menu principal
    if (menu_pressed_zxdesktop_button_which>0) mostrar_first_aid_menu=0;

    if (mostrar_first_aid_menu) menu_first_aid("initial_menu");

	//Si descargar stats
	//Si se pregunta si se quiere enviar estadisticas, solo si esta el grabado de configuracion, e interfaz permite menu (no stdout ni simpletext ni null)
    //Tampoco queremos que aparezca la primera vez que sale tambien el wizard, porque son demasiadas preguntas y molestará al usuario
	if (save_configuration_file_on_exit.v && stats_asked.v==0 && si_normal_menu_video_driver() && !appeared_zesarux_first_start) {
		stats_ask_if_enable();
	}



	int salir_menu=0;


	do {
        //printf("menu_pressed_zxdesktop_configurable_icon_which %d\n",menu_pressed_zxdesktop_configurable_icon_which);
        //printf("antes del if\n");
		if (menu_pressed_zxdesktop_button_which>=0 || menu_pressed_zxdesktop_lower_icon_which>=0
            || menu_pressed_zxdesktop_configurable_icon_which>=0 || menu_pressed_zxdesktop_right_button_background>=0
            || pulsado_alguna_ventana_con_menu_cerrado) {

            DBG_PRINT_ZXVISION_EVENTS VERBOSE_DEBUG,"ZXVISION_EVENTS: menu_inicio_bucle_main. Condition true: "
                "menu_pressed_zxdesktop_button_which %d menu_pressed_zxdesktop_lower_icon_which %d "
                "menu_pressed_zxdesktop_lower_icon_which_right_button %d menu_pressed_zxdesktop_configurable_icon_right_button %d "
                "pulsado_alguna_ventana_con_menu_cerrado %d",
                menu_pressed_zxdesktop_button_which,menu_pressed_zxdesktop_lower_icon_which,
                menu_pressed_zxdesktop_lower_icon_which_right_button,menu_pressed_zxdesktop_configurable_icon_right_button,
                pulsado_alguna_ventana_con_menu_cerrado);

			cls_menu_overlay();
		//Si se habia pulsado boton de zx desktop y boton no es el 0
		//con boton 0 lo que hacemos es abrir el menu solamente
			if (menu_pressed_zxdesktop_button_which>0) {
							menu_inicio_handle_button_presses();
			}

			//Si era 0 es el menu inicio y liberamos el boton
			else if (menu_pressed_zxdesktop_button_which==0) {
                DBG_PRINT_ZXVISION_EVENTS VERBOSE_INFO,"ZXVISION_EVENTS: menu_inicio_bucle_main. Handle upper button presses");
				menu_pressed_zxdesktop_button_which=-1;
			}

			else if (menu_pressed_zxdesktop_lower_icon_which>=0) {
                DBG_PRINT_ZXVISION_EVENTS VERBOSE_INFO,"ZXVISION_EVENTS: menu_inicio_bucle_main. Handle lower button presses");
				menu_inicio_handle_lower_icon_presses();
			}

            else if (menu_pressed_zxdesktop_configurable_icon_which>=0) {
                DBG_PRINT_ZXVISION_EVENTS VERBOSE_INFO,"ZXVISION_EVENTS: menu_inicio_bucle_main. Handle desktop icons presses");
                menu_inicio_handle_configurable_icon_presses();
            }

            else if (menu_pressed_zxdesktop_right_button_background>=0) {
                DBG_PRINT_ZXVISION_EVENTS VERBOSE_INFO,"ZXVISION_EVENTS: menu_inicio_bucle_main. Handle right button background");
                menu_inicio_handle_right_button_background();
            }
			//printf ("despues menu_inicio_handle_button_presses\n");

            else if (pulsado_alguna_ventana_con_menu_cerrado) {
                debug_printf(VERBOSE_DEBUG,"Pressed on a window with menu closed from menu_inicio_bucle_main");
                pulsado_alguna_ventana_con_menu_cerrado=0;
                salir_menu=1;
            }
		}

		else {
            //printf("else if\n");
            zxvision_helper_menu_shortcut_init();
            //zxvision_index_search_init_menu_path();

            //Cada vez que se abre el menu, se genera un ciclo del juego de la vida
            //gamelife_fire_next_event();

            if (strcmp(scr_new_driver_name,"xwindows")==0 || strcmp(scr_new_driver_name,"sdl")==0 || strcmp(scr_new_driver_name,"caca")==0 || strcmp(scr_new_driver_name,"fbdev")==0 || strcmp(scr_new_driver_name,"cocoa")==0 || strcmp(scr_new_driver_name,"curses")==0) f_functions=1;
            else f_functions=0;

            //Mostrar menu principal
            //printf("Antes menu_inicio_mostrar_main_menu\n");
            salir_menu=menu_inicio_mostrar_main_menu(salir_menu);
            //printf("Despues menu_inicio_mostrar_main_menu\n");

		}
    //printf("while en menu_inicio_bucle_main\n");

	} while (!salir_menu && !salir_todos_menus);

//extern zxvision_window *menu_dibuja_submenu_primer_submenu;
    //printf("llamar a menu_dibuja_menu_cierra_todos_submenus desde fin menu_inicio_bucle_main. antes menu_dibuja_submenu_primer_submenu=%p\n",menu_dibuja_submenu_primer_submenu);

    //Necesario para cerrar submenus, por ejemplo si estamos en un item de menu con submenus,
    //y simplemente pulsamos fuera del menu, con lo que se simula pulsado ESC
    //pero deja submenus abiertos
    menu_dibuja_submenu_cierra_todos_submenus();
    //printf("llamar a menu_dibuja_menu_cierra_todos_submenus desde fin menu_inicio_bucle_main. despues menu_dibuja_submenu_primer_submenu=%p\n",menu_dibuja_submenu_primer_submenu);

    //Si se ha lanzado un reindexado entero de todos los menus
    menu_dibuja_menu_recorrer_menus=0;

}


//Esto de momento solo se llama desde una tecla F. Lo pongo aqui para que este cerca de las condiciones y acciones de insert
void menu_reinsert_std_tape(void)
{

	debug_printf(VERBOSE_DEBUG,"Running reinsert tape");

	if (!menu_tape_input_insert_cond() ) {
		debug_printf(VERBOSE_DEBUG,"No tape inserted to reinsert");
		return;
	}

	//Si esta insertada, expulsar
	if ((tape_loadsave_inserted & TAPE_LOAD_INSERTED)) {
		debug_printf(VERBOSE_DEBUG,"Ejecting tape");
		tap_close();
	}

	//E insertar
	debug_printf(VERBOSE_DEBUG,"Inserting tape");
	tap_open();
}


void menu_process_f_function_pause(void)
{

    //TODO: pulsando teclas F, como F5, no se sale de la pausa, pues dicha tecla no se interpreta como tecla en menu_get_pressed_key

	int antes_multitarea;

	//Guardar valor anterior multitarea
	antes_multitarea=menu_multitarea;
	menu_multitarea=0;
	audio_playing.v=0;
	z80_byte tecla=0;

	while (tecla==0) {
		menu_espera_tecla();
		tecla=menu_get_pressed_key();
        //printf("tecla %d\n",tecla);

        //Si se pulsa una tecla, pero que no tiene representacion para menu_get_pressed_key (por ejemplo tecla F)
        //seguiremos en el bucle
        //pero si esto sucede, hay una tecla pulsada (por ejemplo una tecla F es puerto_especial*)
        //y entonces en menu_espera_tecla no llamara a menu_cpu_core_loop, y este es el que llama a scr_actualiza_tablas_teclado(),
        //Esa llamada es importante en drivers de video que hay que llamarlos explicitamente para que lean/liberen teclas y botones ratón,
        //como xwindows (cocoa en cambio salta a la rutina de liberar/pulsar tecla cuando se actua sobre una de ellas)
        //entonces tenemos que llamar a scr_actualiza_tablas_teclado(), o nos quedariamos en un bucle cerrado
        //si al entrar en pausa, pulsamos una tecla F por ejemplo
        scr_actualiza_tablas_teclado();

        //O si se pulsa boton raton, volver
        if (zxvision_clicked_mouse_button()) tecla=13;
	}

	menu_espera_no_tecla();

    //printf("no tecla %d\n",tecla);

	//restaurar
	menu_multitarea=antes_multitarea;
}

void menu_process_f_function_topspeed(void)
{
	timer_toggle_top_speed_timer();

	if (top_speed_timer.v) {
		//Parece que ni footer ni splash aparece pues el menu esta medio abierto
		//En vez de esto meteremos el texto TOPSPEED cuando se actualiza footer
		//generic_footertext_print_operating("TSPEED");
	}
	else {
		//screen_print_splash_text_center(ESTILO_GUI_TINTA_NORMAL,ESTILO_GUI_PAPEL_NORMAL,"Disabling CPU Top speed");
	}
}

int audiovolume_before_silence=100;

//Procesar accion tecla F, o pulsado de boton superior reconfigurado, o pulsado de icono en ZX Desktop
//si_pulsado_icono_zxdesktop=1 en caso de iconos de ZX Desktop
//si_pulsado_icono_zxdesktop=0 en caso de teclas F o botones superior reconfigurado, y para distinguirlos, se usa si_pulsado_boton_redefinido
void menu_process_f_functions_by_action_name(int id_funcion,int si_pulsado_icono_zxdesktop,int id_tecla_f_pulsada,int si_pulsado_boton_redefinido,int numero_boton_redefinido)
{

    //printf("enum: %d\n",id_funcion);

	//char final_name[PATH_MAX];
    int indice_icono;

    //char buffer_mensaje[256];

	switch (id_funcion)
	{
		case F_FUNCION_DEFAULT:
		break;

		case F_FUNCION_NOTHING:
		break;

		case F_FUNCION_RESET:
			reset_cpu();
		break;

		case F_FUNCION_HARDRESET:
			hard_reset_cpu();
		break;

		case F_FUNCION_NMI:
			generate_nmi();
		break;

		case F_FUNCION_OPENMENU:
			if (util_if_open_just_menu() ) {
                menu_inicio_bucle();
                //printf("menu_process_f_functions_by_action_name. despues menu_inicio_bucle\n");
            }
		break;

		case F_FUNCION_OCR:
			textspeech_enviar_speech_pantalla();
		break;

		case F_FUNCION_SMARTLOAD:
			menu_smartload(0);
		break;

		case F_FUNCION_QUICKLOAD:
			menu_snapshot_quickload(0);
		break;

		case F_FUNCION_QUICKSAVE:

			menu_snapshot_quicksave(0);

		break;

		case F_FUNCION_QUICKSAVE_SCREEN:

			menu_display_quicksave_scr(0);

		break;

        case F_FUNCION_OPEN_WINDOW:

            //Abrir ventana que viene especificado como parametro del icono o tecla F.

            //Si viene de pulsar icono
            if (si_pulsado_icono_zxdesktop) {
                indice_icono=zxdesktop_configurable_icons_current_executing;

                if (indice_icono!=-1) {

                    char *nombre=zxdesktop_configurable_icons_list[indice_icono].extra_info;

                    zxvision_open_window_by_name(nombre);

                }
            }

            //Viene de pulsar tecla F o boton ZX Desktop redefinido
            else {
                if (si_pulsado_boton_redefinido) {
                    //debug_printf(VERBOSE_ERR,"This action can only be fired from a ZX Desktop icon or F-key");
                    //printf("Numero boton pulsado: %d\n",numero_boton_redefinido);
                    if (numero_boton_redefinido<0 || numero_boton_redefinido>=MAX_USERDEF_BUTTONS) {
                        debug_printf(VERBOSE_ERR,"Error getting Button parameters");
                    }
                    else {
                        char *nombre=defined_buttons_functions_array_parameters[numero_boton_redefinido];
                        zxvision_open_window_by_name(nombre);
                    }
                }
                else {
                    if (id_tecla_f_pulsada<0) debug_printf(VERBOSE_ERR,"Error getting F-Key info");
                    else {
                        char *nombre=defined_f_functions_keys_array_parameters[id_tecla_f_pulsada];
                        zxvision_open_window_by_name(nombre);
                    }
                }

            }
        break;

        case F_FUNCION_DESKTOP_SNAPSHOT:
        case F_FUNCION_DESKTOP_TAPE:
        case F_FUNCION_DESKTOP_GENERIC_SMARTLOAD:

            //Hacer smartload del archivo que viene especificado como parametro del icono o tecla F.

            //Si viene de pulsar icono
            if (si_pulsado_icono_zxdesktop) {
                indice_icono=zxdesktop_configurable_icons_current_executing;

                if (indice_icono!=-1) {

                    char *nombre=zxdesktop_configurable_icons_list[indice_icono].extra_info;
                    //Si nombre vacio, indicar al usuario
                    if (nombre[0]==0) {
                        debug_printf(VERBOSE_ERR,"You should write a file name on the Icon parameters");
                    }

                    else {

                        strcpy(quickload_file,nombre);

                        quickfile=quickload_file;

                        //Ver si es un zip que viene de una descarga online por ejemplo
                        if (!util_compare_file_extension(nombre,"zip")) {
                            menu_smartload(0);
                        }

                        else {
                            quickload(quickload_file);
                        }
                    }
                }
            }

            //Viene de pulsar tecla F o boton ZX Desktop redefinido
            else {
                if (si_pulsado_boton_redefinido) {
                    if (numero_boton_redefinido<0 || numero_boton_redefinido>=MAX_USERDEF_BUTTONS) {
                        debug_printf(VERBOSE_ERR,"Error getting Button parameters");
                    }
                    else {
                        char *nombre=defined_buttons_functions_array_parameters[numero_boton_redefinido];

                        strcpy(quickload_file,nombre);

                        quickfile=quickload_file;

                        //Ver si es un zip que viene de una descarga online por ejemplo
                        if (!util_compare_file_extension(nombre,"zip")) {
                            menu_smartload(0);
                        }

                        else {
                            quickload(quickload_file);
                        }
                    }


                    //No hay parametros. Abrir smartload tal cual
                    //printf("Pulsado boton redefinido\n");
                    //menu_smartload(0);
                }
                else {
                    if (id_tecla_f_pulsada<0) debug_printf(VERBOSE_ERR,"Error getting F-Key info");
                    else {
                        char *nombre=defined_f_functions_keys_array_parameters[id_tecla_f_pulsada];
                        strcpy(quickload_file,nombre);

                        quickfile=quickload_file;

                        //Ver si es un zip que viene de una descarga online por ejemplo
                        if (!util_compare_file_extension(nombre,"zip")) {
                            menu_smartload(0);
                        }

                        else {
                            quickload(quickload_file);
                        }
                    }
                }

            }
        break;

        case F_FUNCION_DESKTOP_MY_MACHINE:
            menu_debug_machine_info(0);
        break;

        case F_FUNCION_REWIND:
            snapshot_in_ram_rewind();
        break;

        case F_FUNCION_FFW:
            snapshot_in_ram_ffw();
        break;

		case F_FUNCION_LOADBINARY:
			menu_debug_load_binary(0);
		break;

		case F_FUNCION_SAVEBINARY:
			menu_debug_save_binary(0);
		break;

		case F_FUNCION_SETTINGS:
			menu_settings(0);
		break;

		case F_FUNCION_WAVEFORM:
			menu_audio_new_waveform(0);
		break;

		case F_FUNCION_AUDIO_REGISTERS:
			menu_ay_registers(0);
		break;

		case F_FUNCION_AUDIO_SHEET:
			menu_ay_partitura(0);
		break;

		case F_FUNCION_AUDIO_PIANO:
			menu_ay_pianokeyboard(0);
		break;

        case F_FUNCION_WAVE_PIANO:
            menu_beeper_pianokeyboard(0);
        break;

        case F_FUNCION_AYMIXER:
            menu_ay_mixer(0);
        break;

        case F_FUNCION_AYPLAYER:
            menu_audio_new_ayplayer(0);
        break;

        case F_FUNCION_AYPLAYER_PREVIOUS_TRACK:
            ay_player_previous_track();
        break;

        case F_FUNCION_AYPLAYER_NEXT_TRACK:
            ay_player_next_track();
        break;

        case F_FUNCION_AYPLAYER_PREVIOUS_FILE:
            ay_player_previous_file();
        break;

        case F_FUNCION_AYPLAYER_NEXT_FILE:
            ay_player_next_file();
        break;

        case F_FUNCION_AYPLAYER_PAUSE:
            ay_player_pause_unpause();
        break;

        case F_FUNCION_VOLUME_DECREMENT:
            audiovolume -=10;
            if (audiovolume<=0) audiovolume=0;

            //Parece que no se ve mensaje de splash porque se abre y se cierra el menu
            //sprintf(buffer_mensaje,"Volume set to %d %%",audiovolume);
            //screen_print_splash_text_center_no_if_previous(ESTILO_GUI_TINTA_NORMAL,ESTILO_GUI_PAPEL_NORMAL,buffer_mensaje);

            //Lo mismo pasa con mensajes en el footer
            //sprintf(buffer_mensaje,"V%d",audiovolume);
            //generic_footertext_print_operating(buffer_mensaje);
        break;

        case F_FUNCION_VOLUME_INCREMENT:
            audiovolume +=10;
            if (audiovolume>=100) audiovolume=100;
        break;

        case F_FUNCION_VOLUME_SILENCE:
            if (audiovolume==0) audiovolume=audiovolume_before_silence;
            else {
                audiovolume_before_silence=audiovolume;
                audiovolume=0;
            }
        break;

        case F_FUNCION_COLOUR_PALETTES:
            menu_display_total_palette(0);
        break;

        case F_FUNCION_HEX_EDITOR:
            menu_debug_hexdump(0);
        break;

        case F_FUNCION_VIEW_SPRITES:
            menu_debug_view_sprites(0);
        break;

        case F_FUNCION_FILE_UTILITIES:
            menu_debug_file_utils(0);
        break;

        //Gestiono las dos acciones desde aqui porque son casi iguales
        case F_FUNCION_SET_MACHINE:
        case F_FUNCION_MACHINE_SELECTION:

            //Elegir maquina si viene especificado como parametro del icono o tecla F. Y si no, abrir menu de seleccion de maquina

            //Si viene de pulsar icono
            if (si_pulsado_icono_zxdesktop) {
                //printf("Pulsado icono\n");
                indice_icono=zxdesktop_configurable_icons_current_executing;

                if (indice_icono!=-1) {
                    //Si opciones en blanco
                    if (zxdesktop_configurable_icons_list[indice_icono].extra_info[0]==0) {
                        menu_machine_selection(0);
                    }
                    else {
                        int maquina=get_machine_id_by_name(zxdesktop_configurable_icons_list[indice_icono].extra_info);
                        if (maquina==-1) return;

                        menu_machine_set_machine_by_id(maquina);
                    }
                }
            }

            //Viene de pulsar tecla F o boton ZX Desktop redefinido
            else {
                if (si_pulsado_boton_redefinido) {
                    //printf("Pulsado boton redefinido\n");

                    if (numero_boton_redefinido<0 || numero_boton_redefinido>=MAX_USERDEF_BUTTONS) {
                        debug_printf(VERBOSE_ERR,"Error getting Button parameters");
                    }
                    else {
                        char *nombre=defined_buttons_functions_array_parameters[numero_boton_redefinido];
                        //Si opciones en blanco
                        if (nombre[0]==0) {
                            menu_machine_selection(0);
                        }
                        else {
                            int maquina=get_machine_id_by_name(nombre);
                            if (maquina==-1) return;

                            menu_machine_set_machine_by_id(maquina);
                        }
                    }


                }

                else {

                    //printf("Pulsado tecla F\n");
                    if (id_tecla_f_pulsada<0) debug_printf(VERBOSE_ERR,"Error getting F-Key info");
                    else {
                        //printf("tecla F %d\n",id_tecla_f_pulsada);
                        //printf("Parametros: %s\n",defined_f_functions_keys_array_parameters[id_tecla_f_pulsada]);
                        //Si opciones en blanco
                        if (defined_f_functions_keys_array_parameters[id_tecla_f_pulsada][0]==0) {
                            menu_machine_selection(0);
                        }

                        else  {
                            int maquina=get_machine_id_by_name(defined_f_functions_keys_array_parameters[id_tecla_f_pulsada]);
                            if (maquina==-1) return;

                            menu_machine_set_machine_by_id(maquina);
                        }

                    }

                }

            }


        break;

		case F_FUNCION_ZENG_SENDMESSAGE:
			if (menu_zeng_send_message_cond()) menu_zeng_send_message(0);
		break;

		case F_FUNCION_ZENG_ONLINE:
			menu_zeng_online(0);
		break;

		case F_FUNCION_ZENG_ONLINE_SENDMESSAGE:
			menu_zeng_online_write_message_room(0);
		break;

        case F_FUNCION_ONLINE_SPECCY:
            menu_online_browse_zxinfowos(0);
        break;

        case F_FUNCION_ONLINE_ZX81:
            menu_online_browse_zx81(0);
        break;

		case F_FUNCION_OSDKEYBOARD:
			menu_onscreen_keyboard(0);
		break;

		case F_FUNCION_OSDTEXTKEYBOARD:
			menu_osd_adventure_keyboard(0);
		break;

        case F_FUNCION_LEFTRIGHT_JOY:
            joystick_autoleftright_enabled ^=1;
        break;

		case F_FUNCION_SWITCHBORDER:
			if (menu_interface_border_cond() ) menu_interface_border(0);
		break;

		case F_FUNCION_SWITCHFULLSCREEN:
			menu_interface_fullscreen(0);
		break;

		case F_FUNCION_RELOADMMC:
            //TODO: solo hace reload de tarjeta primera
			mmc_read_file_to_memory(0);
		break;

		case F_FUNCION_REINSERTSTDTAPE:
			menu_reinsert_std_tape();
		break;

		case F_FUNCION_PAUSEUNPAUSEREALTAPE:
			menu_realtape_pause_unpause(0);
		break;

		case F_FUNCION_REINSERTREALTAPE:
			menu_reinsert_real_tape();
		break;

		case F_FUNCION_REWINDREALTAPE:
			realtape_rewind_five();
		break;

		case F_FUNCION_FFWDREALTAPE:
			realtape_ffwd_five();
		break;

		case F_FUNCION_DEBUGCPU:
			menu_debug_registers(0);
            /*zxvision_restore_one_window("debugcpu");


            zxvision_window *ventana_debug_cpu=zxvision_find_window_in_background("debugcpu");

            if (ventana_debug_cpu!=NULL) zxvision_handle_mouse_ev_switch_back_wind(ventana_debug_cpu);

            menu_abierto=1;*/
		break;

		case F_FUNCION_DEBUGCPU_BREAKPOINTS:
			menu_breakpoints(0);
		break;

		case F_FUNCION_DEBUGCPU_VIEW_ADVENTURE:
			menu_debug_registers_view_adventure(0);
		break;

        case F_FUNCION_POKE:
            menu_poke(0);
        break;

		case F_FUNCION_SWITCHFOOTER:
            menu_interface_footer(0);
		break;

        case F_FUNCION_VISUALREALTAPE:
            menu_visual_realtape(0);
        break;

		case F_FUNCION_TEXT_ADVENTURE_MAP:
			menu_debug_textadventure_map_connections(0);
		break;

		case F_FUNCION_PAUSE:
			menu_process_f_function_pause();
		break;

		case F_FUNCION_TOPSPEED:
			menu_process_f_function_topspeed();
		break;

		case F_FUNCION_EXITEMULATOR:
			end_emulator_autosave_snapshot();
		break;

		case F_FUNCION_OVERLAY_WINDOWS:
			//Solo permitido si esta el setting de background windows
			if (!menu_allow_background_windows) {
                menu_error_message("Background windows setting is not enabled. You can enable it on Settings-> ZX Vision-> Background windows");
        	}
			else {
				//Solo si opcion esta desactivada
				//Y solo desde el menu. Aunque para llegar aqui se abre el menu, con lo que el check es redundante
				if (!overlay_visible_when_menu_closed && menu_abierto) {
					overlay_visible_when_menu_closed=1;
					debug_printf(VERBOSE_INFO,"Enabling ShowBackgroundWindows");
				}
				//Al lanzar menu de inicio siempre se pone overlay_visible_when_menu_closed=0,
				//por lo que al llegar aqui siempre estar a 0 y se activara
				//Por tanto para activar hay que pulsar la tecla F asociada,
				//pero para desactivarlo hay que pulsar cualquier otra tecla F (y no la misma, o se reactivaria)
			}
		break;

        case F_FUNCION_ZXUNO_PRISM:
            if (MACHINE_IS_ZXUNO) {
                zxuno_ports[0x50] ^= 128;
                //No sirve aqui de nada lanzar el splash, porque en este momento el menu se ha abierto y los splash no se ven
                //zxuno_prism_mode_splash();
            }
        break;

		case F_FUNCION_DESKTOP_TRASH:
			menu_zxdesktop_trash(0);
		break;


	}

}





//
// Inicio funciones de dispositivos especificos en el ZX Desktop
//

//Funciones para cinta de cassette

int zxdesktop_lowericon_cassete_is_visible(void)
{
	if (!MACHINE_IS_Z88 && !MACHINE_IS_QL && !MACHINE_IS_CHLOE && !MACHINE_IS_COLECO
        && !MACHINE_IS_SG1000 && !MACHINE_IS_SMS && !MACHINE_IS_PCW) return 1;
	else return 0;
}

int zxdesktop_lowericon_cassete_is_active(void)
{
    return is_tape_inserted();
}

int zxdesktop_lowericon_cassete_real_is_active(void)
{
	if (realtape_inserted.v) return 1;
	else return 0;
}

void zxdesktop_lowericon_cassete_accion(void)
{
	menu_storage_tape(0);
}

void zxdesktop_lowericon_cassete_std_accion_boton_derecho(void)
{

    int tape_is_inserted=is_tape_inserted();

    char buffer_insert[20];
    if (tape_is_inserted) strcpy(buffer_insert,"Eject tape");
    else strcpy(buffer_insert,"Open...");

    int opcion;

    if (menu_tape_input_insert_cond()) {
        opcion=menu_simple_two_choices("Standard Tape","--Action--",buffer_insert,"Reinsert tape");
    }
    else {
        opcion=menu_simple_one_choices("Standard Tape","--Action--",buffer_insert);
    }

    switch (opcion) {
        case 1:
            if (tape_is_inserted) menu_tape_input_insert(0);
            else menu_tape_open(0);
        break;

        case 2:
            menu_reinsert_std_tape();
        break;


    }


}

void zxdesktop_lowericon_cassete_real_accion_boton_derecho(void)
{

    char buffer_insert[20];
    if (realtape_inserted.v) strcpy(buffer_insert,"Eject tape");
    else strcpy(buffer_insert,"Open...");

    int opcion;

    if (menu_realtape_inserted_cond()) {
        char buffer_play[20];
        if (realtape_playing.v) strcpy(buffer_play,"Pause");
        else strcpy(buffer_play,"Play");

        opcion=menu_simple_three_choices("Real Tape","--Action--",buffer_insert,"Reinsert tape",buffer_play);

    }
    else {
        if (menu_realtape_cond()) {
            opcion=menu_simple_two_choices("Real Tape","--Action--",buffer_insert,"Reinsert tape");
        }
        //Solo permite abrir cinta
        else {
            opcion=menu_simple_one_choices("Real Tape","--Action--",buffer_insert);
        }
    }

    switch (opcion) {

        case 1:
            if (realtape_inserted.v) menu_realtape_insert(0);
            else menu_realtape_open(0);
        break;



        case 2:
           menu_reinsert_real_tape();
        break;

        case 3:
            menu_realtape_pause_unpause(0);
        break;

    }


}

//Funciones para floppy +3


int zxdesktop_lowericon_plus3_flp_is_visible(void)
{
	if (MACHINE_IS_SPECTRUM_P2A_P3 || MACHINE_IS_CPC_HAS_FLOPPY || MACHINE_IS_PCW) return 1;
	else return 0;
}

int zxdesktop_lowericon_plus3_flp_is_active(void)
{
	if (dskplusthree_emulation.v) return 1;
	else return 0;
}

void zxdesktop_lowericon_plus3_flp_accion(void)
{
	menu_plusthreedisk(0);
}

//Funciones para Betadisk


int zxdesktop_lowericon_betadisk_is_visible(void)
{
	if (MACHINE_IS_SPECTRUM) return 1;
	else return 0;
}

int zxdesktop_lowericon_betadisk_is_active(void)
{
	if (trd_enabled.v) return 1;
	else return 0;
}

void zxdesktop_lowericon_betadisk_accion(void)
{
	menu_betadisk(0);
}

void zxdesktop_lowericon_betadisk_accion_boton_derecho(void)
{
    char buffer_insert[20];
    if (trd_enabled.v) strcpy(buffer_insert,"Eject disk");
    else strcpy(buffer_insert,"Open...");

    int opcion=menu_simple_one_choices("Betadisk","--Action--",buffer_insert);

    switch (opcion) {
        case 1:
            if (trd_enabled.v) menu_storage_trd_emulation(0);
            else menu_storage_trd_file(0);
        break;

    }
}

void zxdesktop_lowericon_plus3_flp_accion_boton_derecho(void)
{
    char buffer_insert[20];
    if (dskplusthree_emulation.v) strcpy(buffer_insert,"Eject disk");
    else strcpy(buffer_insert,"Open...");

    int opcion=menu_simple_one_choices("3\" CF2 Floppy","--Action--",buffer_insert);

    switch (opcion) {
        case 1:
            if (dskplusthree_emulation.v) menu_storage_dskplusthree_emulation(0);
            else menu_storage_dskplusthree_file(0);
        break;

    }
}

//Funciones para MMC

int zxdesktop_lowericon_mmc_is_visible(void)
{
	if (MACHINE_IS_SPECTRUM) return 1;
	else return 0;
}

int zxdesktop_lowericon_mmc_is_visible_second(void)
{
	if (MACHINE_IS_SPECTRUM) {
        //Visible la segunda tarjeta si esta habilitada,
        //o si bien esta habilitada la primera, para dar opcion a habilitar una segunda
        //Pero si no está ni la primera, no aparece
        if (mmc_enabled[0].v || mmc_enabled[1].v) return 1;
        else return 0;
    }
	else return 0;
}

int zxdesktop_lowericon_mmc_is_active(void)
{
	if (mmc_enabled[0].v) return 1;
	else return 0;
}

int zxdesktop_lowericon_mmc_is_active_second(void)
{
	if (mmc_enabled[1].v) return 1;
	else return 0;
}

void zxdesktop_lowericon_mmc_accion(void)
{
	menu_mmc_divmmc(0);
}



void zxdesktop_lowericon_mmc_accion_boton_derecho_common(int tarjeta)
{
    char buffer_insert[20];
    if (mmc_enabled[tarjeta].v) strcpy(buffer_insert,"Eject MMC");
    else strcpy(buffer_insert,"Open...");

    int opcion=menu_simple_one_choices("MMC","--Action--",buffer_insert);

    switch (opcion) {
        case 1:
            if (mmc_enabled[tarjeta].v) menu_storage_mmc_emulation(tarjeta);
            else {
                //Insertar archivo y habilitar
                menu_storage_mmc_file(tarjeta);
                //printf("File: [%s]\n",mmc_file_name);
                //Si no se ha dejado archivo en blanco (salido con ESC)
                if (mmc_file_name[tarjeta][0]) {
                    menu_storage_mmc_emulation(tarjeta);
                    //Y si no estan los puertos divmmc, habilitarlos tambien (se deshabilitan al desactivar mmc)
                    if (divmmc_mmc_ports_enabled.v==0) menu_storage_divmmc_mmc_ports_emulation(0);
                }
            }
        break;

    }

}

void zxdesktop_lowericon_mmc_accion_boton_derecho(void)
{
    zxdesktop_lowericon_mmc_accion_boton_derecho_common(0);

}

void zxdesktop_lowericon_mmc_accion_boton_derecho_second(void)
{
    zxdesktop_lowericon_mmc_accion_boton_derecho_common(1);

}

//Funciones para IDE

int zxdesktop_lowericon_ide_is_visible(void)
{
	if (MACHINE_IS_SPECTRUM || MACHINE_IS_SAM) return 1;
	else return 0;
}

int zxdesktop_lowericon_ide_is_active(void)
{
	if (ide_enabled.v) return 1;
	else return 0;
}

void zxdesktop_lowericon_ide_accion(void)
{
	menu_ide_divide(0);
}

void zxdesktop_lowericon_ide_accion_boton_derecho(void)
{
    char buffer_insert[20];
    if (ide_enabled.v) strcpy(buffer_insert,"Eject IDE");
    else strcpy(buffer_insert,"Open...");

    int opcion=menu_simple_one_choices("IDE","--Action--",buffer_insert);

    switch (opcion) {
        case 1:
            if (ide_enabled.v) menu_storage_ide_emulation(0);
            else {
                //Insertar archivo y habilitar
                menu_storage_ide_file(0);
                //printf("file: [%s]\n",ide_file_name);
                //Si no se ha dejado archivo en blanco (salido con ESC)
                if (ide_file_name[0]) {
                    menu_storage_ide_emulation(0);
                    //Y si no estan los puertos divide, habilitarlos tambien (se deshabilitan al desactivar ide)
                    if (divide_ide_ports_enabled.v==0) menu_storage_divide_ide_ports_emulation(0);
                }
            }
        break;

    }

}

//Funciones para zxpand

int zxdesktop_lowericon_zxpand_is_visible(void)
{
	if (MACHINE_IS_ZX8081) return 1;
	else return 0;
}

int zxdesktop_lowericon_zxpand_is_active(void)
{
	if (zxpand_enabled.v) return 1;
	else return 0;
}

void zxdesktop_lowericon_zxpand_accion(void)
{
	menu_zxpand(0);
}

//Funciones para Cartuchos TS 2068

int zxdesktop_lowericon_cart_timex_is_visible(void)
{
	if (MACHINE_IS_TIMEX_TS_TC_2068) return 1;

	else return 0;
}

int zxdesktop_lowericon_cart_timex_is_active(void)
{
	if (timex_cartridge_inserted.v) return 1;
	else return 0;
}

void zxdesktop_lowericon_cart_timex_accion(void)
{
	menu_timexcart(0);
}

//Funciones para dandanator
int zxdesktop_lowericon_dandanator_is_visible(void)
{
	if (MACHINE_IS_SPECTRUM || MACHINE_IS_CPC) return 1;

	else return 0;
}

int zxdesktop_lowericon_dandanator_is_active(void)
{
	if (dandanator_enabled.v) return 1;
	else return 0;
}

void zxdesktop_lowericon_dandanator_accion(void)
{
    menu_dandanator(0);
}

void zxdesktop_lowericon_dandanator_accion_boton_derecho(void)
{
    if (dandanator_enabled.v) {
        int opcion=menu_simple_one_choices("ZX Dandanator","--Action--","Press Button");

        switch (opcion) {


            case 1:
                dandanator_press_button();
            break;


        }
    }
    else {
        menu_warn_message("ZX Dandanator not enabled");
    }
}

//Funciones para hilow
int zxdesktop_lowericon_hilow_is_visible(void)
{
	if (MACHINE_IS_SPECTRUM) return 1;

	else return 0;
}

int zxdesktop_lowericon_hilow_is_active(void)
{
	if (hilow_enabled.v) return 1;
	else return 0;
}

void zxdesktop_lowericon_hilow_accion(void)
{
    menu_hilow(0);
}

void zxdesktop_lowericon_hilow_accion_boton_derecho(void)
{
    int opcion;

    if (hilow_enabled.v) {
        opcion=menu_simple_two_choices("Hilow DataDrive","--Action--","Switch tape inserted flag","NMI");
    }
    else {
        opcion=menu_simple_one_choices("Hilow DataDrive","--Action--","Switch tape inserted flag");
    }

    switch (opcion) {


        case 1:
            menu_storage_hilow_insert(0);
        break;

        case 2:
            generate_nmi();
        break;


    }
}

//Funciones para zxuno flash
int zxdesktop_lowericon_zxunoflash_is_visible(void)
{
	if (MACHINE_IS_ZXUNO) return 1;

	else return 0;
}

int zxdesktop_lowericon_zxunoflash_is_active(void)
{
	if (MACHINE_IS_ZXUNO) return 1;
	else return 0;
}

void zxdesktop_lowericon_zxunoflash_accion(void)
{
    menu_zxuno_spi_flash(0);
}


//Funciones para ZXMMC+ flash
int zxdesktop_lowericon_zxmmcplusflash_is_visible(void)
{
	if (MACHINE_IS_SPECTRUM && zxmmcplus_enabled.v) return 1;

	else return 0;
}

int zxdesktop_lowericon_zxmmcplusflash_is_active(void)
{
	if (MACHINE_IS_SPECTRUM && zxmmcplus_enabled.v) return 1;
	else return 0;
}

void zxdesktop_lowericon_zxmmcplusflash_accion(void)
{
    menu_zxmmcplus(0);
}

//Funciones para Cartuchos MSX, Coleco, SVI, SG1000, SMS


void zxdesktop_lowericon_cartridge_accion(void)
{
	menu_msxcart(0);
}



int zxdesktop_lowericon_cartridge_msx_is_visible(void)
{
	if (MACHINE_IS_MSX) return 1;

	else return 0;
}

int zxdesktop_lowericon_cartridge_msx_is_active(void)
{
	if (msx_cartridge_inserted.v) return 1;
	else return 0;
}


int zxdesktop_lowericon_cartridge_svi_is_visible(void)
{
	if (MACHINE_IS_SVI) return 1;

	else return 0;
}

int zxdesktop_lowericon_cartridge_svi_is_active(void)
{
	if (svi_cartridge_inserted.v) return 1;
	else return 0;
}

int zxdesktop_lowericon_cartridge_coleco_is_visible(void)
{
	if (MACHINE_IS_COLECO) return 1;

	else return 0;
}

int zxdesktop_lowericon_cartridge_coleco_is_active(void)
{
	if (coleco_cartridge_inserted.v) return 1;
	else return 0;
}


int zxdesktop_lowericon_cartridge_sg1000_is_visible(void)
{
	if (MACHINE_IS_SG1000) return 1;

	else return 0;
}

int zxdesktop_lowericon_cartridge_sg1000_is_active(void)
{
	if (sg1000_cartridge_inserted.v) return 1;
	else return 0;
}

int zxdesktop_lowericon_cartridge_sms_is_visible(void)
{
	if (MACHINE_IS_SMS) return 1;

	else return 0;
}

int zxdesktop_lowericon_cartridge_sms_is_active(void)
{
	if (sms_cartridge_inserted.v) return 1;
	else return 0;
}


//Funciones para MDV QL/Spectrum y Floppy QL

int zxdesktop_lowericon_mdv1_is_visible(void)
{
	if (MACHINE_IS_QL || MACHINE_IS_SPECTRUM) return 1;

	else return 0;
}

int zxdesktop_lowericon_mdv2_is_visible(void)
{
	if (MACHINE_IS_QL) return 1;

    if (MACHINE_IS_SPECTRUM && microdrive_status[1].microdrive_enabled) return 1;

	else return 0;
}

int zxdesktop_lowericon_mdv3_is_visible(void)
{
    if (MACHINE_IS_SPECTRUM && microdrive_status[2].microdrive_enabled) return 1;

	else return 0;
}

int zxdesktop_lowericon_mdv4_is_visible(void)
{
    if (MACHINE_IS_SPECTRUM && microdrive_status[3].microdrive_enabled) return 1;

	else return 0;
}

int zxdesktop_lowericon_flp1_is_visible(void)
{
	if (MACHINE_IS_QL) return 1;

	else return 0;
}

int zxdesktop_lowericon_mdv1_is_active(void)
{
    if (MACHINE_IS_QL) {
	    if (ql_microdrive_floppy_emulation && ql_device_mdv1_enabled) return 1;

	    else return 0;
    }

    else {
        return microdrive_status[0].microdrive_enabled;
    }
}

int zxdesktop_lowericon_mdv2_is_active(void)
{
    if (MACHINE_IS_QL) {
        if (ql_microdrive_floppy_emulation && ql_device_mdv2_enabled) return 1;
        else return 0;
    }

    else {
        return microdrive_status[1].microdrive_enabled;
    }
}

int zxdesktop_lowericon_mdv3_is_active(void)
{
    return microdrive_status[2].microdrive_enabled;

}

int zxdesktop_lowericon_mdv4_is_active(void)
{
    return microdrive_status[3].microdrive_enabled;

}

int zxdesktop_lowericon_flp1_is_active(void)
{
	if (ql_microdrive_floppy_emulation && ql_device_flp1_enabled) return 1;
	else return 0;
}

void zxdesktop_lowericon_mdv_flp_accion(void)
{
	if (MACHINE_IS_QL) menu_ql_mdv_flp(0);
    else menu_interface1(0);
}


// Funciones para cartuchos Z88

int zxdesktop_lowericon_z88_cart_is_visible(void)
{
	if (MACHINE_IS_Z88) return 1;

	else return 0;
}

int zxdesktop_lowericon_z88_cart_N_is_active(int slot)
{
	if (z88_memory_slots[slot].size!=0) return 1;
	else return 0;
}

int zxdesktop_lowericon_z88_cart_1_is_active(void)
{
	return zxdesktop_lowericon_z88_cart_N_is_active(1);
}

int zxdesktop_lowericon_z88_cart_2_is_active(void)
{
	return zxdesktop_lowericon_z88_cart_N_is_active(2);
}

int zxdesktop_lowericon_z88_cart_3_is_active(void)
{
	return zxdesktop_lowericon_z88_cart_N_is_active(3);
}

void zxdesktop_lowericon_z88_cart_1_accion(void)
{
	menu_z88_slots(1);
}

void zxdesktop_lowericon_z88_cart_2_accion(void)
{
	menu_z88_slots(2);
}


void zxdesktop_lowericon_z88_cart_3_accion(void)
{
	menu_z88_slots(3);
}

//Para iconos que no alteran su inverso, apuntar a aqui
int zxdesktop_common_icon_no_inverse=0;

//Variables que indican actividad de ese icono (se dibujan en color inverso)
int zxdesktop_icon_tape_inverse=0;

//este no se usa, se deja por requisitos de la estructura zdesktop_lowericons_array
int zxdesktop_icon_tape_real_inverse=0;

int zxdesktop_icon_mmc_inverse=0;
int zxdesktop_icon_mmc_inverse_second=0;
int zxdesktop_icon_plus3_inverse=0;
int zxdesktop_icon_betadisk_inverse=0;
int zxdesktop_icon_ide_inverse=0;
int zxdesktop_icon_zxpand_inverse=0;
int zxdesktop_icon_mdv1_inverse=0;
int zxdesktop_icon_mdv2_inverse=0;
int zxdesktop_icon_mdv3_inverse=0;
int zxdesktop_icon_mdv4_inverse=0;
int zxdesktop_icon_flp1_inverse=0;
int zxdesktop_icon_dandanator_inverse=0;
int zxdesktop_icon_zxunoflash_inverse=0;
int zxdesktop_icon_zxmmcplusflash_inverse=0;
int zxdesktop_icon_hilow_inverse=0;


struct s_zxdesktop_lowericons_info zdesktop_lowericons_array[TOTAL_ZXDESKTOP_MAX_LOWER_ICONS]={
	//cinta standard
	{ zxdesktop_lowericon_cassete_is_visible, zxdesktop_lowericon_cassete_is_active,zxdesktop_lowericon_cassete_accion,
        zxdesktop_lowericon_cassete_std_accion_boton_derecho,
		bitmap_lowericon_ext_desktop_cassette_std_active,bitmap_lowericon_ext_desktop_cassette_std_inactive,&zxdesktop_icon_tape_inverse},

	//cinta real
	{ zxdesktop_lowericon_cassete_is_visible, zxdesktop_lowericon_cassete_real_is_active,zxdesktop_lowericon_cassete_accion,
        zxdesktop_lowericon_cassete_real_accion_boton_derecho,
		bitmap_lowericon_ext_desktop_cassette_active,bitmap_lowericon_ext_desktop_cassette_inactive,&zxdesktop_icon_tape_real_inverse},

	//MDV1
	{ zxdesktop_lowericon_mdv1_is_visible, zxdesktop_lowericon_mdv1_is_active, zxdesktop_lowericon_mdv_flp_accion,NULL,
		bitmap_lowericon_ext_desktop_mdv_active,bitmap_lowericon_ext_desktop_mdv_inactive,&zxdesktop_icon_mdv1_inverse},

    //MDV2
	{ zxdesktop_lowericon_mdv2_is_visible, zxdesktop_lowericon_mdv2_is_active, zxdesktop_lowericon_mdv_flp_accion,NULL,
		bitmap_lowericon_ext_desktop_mdv_active,bitmap_lowericon_ext_desktop_mdv_inactive,&zxdesktop_icon_mdv2_inverse},

	//MDV3
	{ zxdesktop_lowericon_mdv3_is_visible, zxdesktop_lowericon_mdv3_is_active, zxdesktop_lowericon_mdv_flp_accion,NULL,
		bitmap_lowericon_ext_desktop_mdv_active,bitmap_lowericon_ext_desktop_mdv_inactive,&zxdesktop_icon_mdv3_inverse},

    //MDV4
	{ zxdesktop_lowericon_mdv4_is_visible, zxdesktop_lowericon_mdv4_is_active, zxdesktop_lowericon_mdv_flp_accion,NULL,
		bitmap_lowericon_ext_desktop_mdv_active,bitmap_lowericon_ext_desktop_mdv_inactive,&zxdesktop_icon_mdv4_inverse},

    //Floppy QL.
	{ zxdesktop_lowericon_flp1_is_visible, zxdesktop_lowericon_flp1_is_active, zxdesktop_lowericon_mdv_flp_accion,NULL,
		bitmap_lowericon_ext_desktop_flp_active,bitmap_lowericon_ext_desktop_flp_inactive,&zxdesktop_icon_flp1_inverse},

	//floppy +3
	{ zxdesktop_lowericon_plus3_flp_is_visible, zxdesktop_lowericon_plus3_flp_is_active,zxdesktop_lowericon_plus3_flp_accion,
    zxdesktop_lowericon_plus3_flp_accion_boton_derecho,
		bitmap_lowericon_ext_desktop_plus3_flp_active,bitmap_lowericon_ext_desktop_plus3_flp_inactive,&zxdesktop_icon_plus3_inverse},

	//betadisk
	{ zxdesktop_lowericon_betadisk_is_visible, zxdesktop_lowericon_betadisk_is_active,zxdesktop_lowericon_betadisk_accion,
        zxdesktop_lowericon_betadisk_accion_boton_derecho,
		bitmap_lowericon_ext_desktop_betadisk_active,bitmap_lowericon_ext_desktop_betadisk_inactive,&zxdesktop_icon_betadisk_inverse},

	//MMC
	{ zxdesktop_lowericon_mmc_is_visible, zxdesktop_lowericon_mmc_is_active, zxdesktop_lowericon_mmc_accion,
        zxdesktop_lowericon_mmc_accion_boton_derecho,
		bitmap_lowericon_ext_desktop_mmc_active,bitmap_lowericon_ext_desktop_mmc_inactive,&zxdesktop_icon_mmc_inverse},

	//MMC segunda tarjeta
	{ zxdesktop_lowericon_mmc_is_visible_second, zxdesktop_lowericon_mmc_is_active_second, zxdesktop_lowericon_mmc_accion,
        zxdesktop_lowericon_mmc_accion_boton_derecho_second,
		bitmap_lowericon_ext_desktop_mmc_active,bitmap_lowericon_ext_desktop_mmc_inactive,&zxdesktop_icon_mmc_inverse_second},

	//IDE
	{ zxdesktop_lowericon_ide_is_visible, zxdesktop_lowericon_ide_is_active, zxdesktop_lowericon_ide_accion,
        zxdesktop_lowericon_ide_accion_boton_derecho,
		bitmap_lowericon_ext_desktop_ide_active,bitmap_lowericon_ext_desktop_ide_inactive,&zxdesktop_icon_ide_inverse},

	//ZXPand
	{ zxdesktop_lowericon_zxpand_is_visible, zxdesktop_lowericon_zxpand_is_active, zxdesktop_lowericon_zxpand_accion,NULL,
		bitmap_lowericon_ext_desktop_mmc_active,bitmap_lowericon_ext_desktop_mmc_inactive,&zxdesktop_icon_zxpand_inverse},

	//Cartuchos msx, coleco, svi, sg1000, sms
	{ zxdesktop_lowericon_cartridge_msx_is_visible, zxdesktop_lowericon_cartridge_msx_is_active, zxdesktop_lowericon_cartridge_accion,NULL,
		bitmap_lowericon_ext_desktop_msx_cart_active,bitmap_lowericon_ext_desktop_msx_cart_inactive,&zxdesktop_common_icon_no_inverse},

	{ zxdesktop_lowericon_cartridge_svi_is_visible, zxdesktop_lowericon_cartridge_svi_is_active, zxdesktop_lowericon_cartridge_accion,NULL,
		bitmap_lowericon_ext_desktop_svi_active,bitmap_lowericon_ext_desktop_svi_inactive,&zxdesktop_common_icon_no_inverse},

	{ zxdesktop_lowericon_cartridge_coleco_is_visible, zxdesktop_lowericon_cartridge_coleco_is_active, zxdesktop_lowericon_cartridge_accion,NULL,
		bitmap_lowericon_ext_desktop_coleco_active,bitmap_lowericon_ext_desktop_coleco_inactive,&zxdesktop_common_icon_no_inverse},

	{ zxdesktop_lowericon_cartridge_sg1000_is_visible, zxdesktop_lowericon_cartridge_sg1000_is_active, zxdesktop_lowericon_cartridge_accion,NULL,
		bitmap_lowericon_ext_desktop_sg1000_active,bitmap_lowericon_ext_desktop_sg1000_inactive,&zxdesktop_common_icon_no_inverse},

	{ zxdesktop_lowericon_cartridge_sms_is_visible, zxdesktop_lowericon_cartridge_sms_is_active, zxdesktop_lowericon_cartridge_accion,NULL,
		bitmap_lowericon_ext_desktop_sms_active,bitmap_lowericon_ext_desktop_sms_inactive,&zxdesktop_common_icon_no_inverse},

	//Cartuchos Timex TS2068
	{ zxdesktop_lowericon_cart_timex_is_visible, zxdesktop_lowericon_cart_timex_is_active, zxdesktop_lowericon_cart_timex_accion,NULL,
		bitmap_lowericon_ext_desktop_cart_timex_active,bitmap_lowericon_ext_desktop_cart_timex_inactive,&zxdesktop_common_icon_no_inverse},


	//3 Cartuchos de Z88.
	{ zxdesktop_lowericon_z88_cart_is_visible, zxdesktop_lowericon_z88_cart_1_is_active, zxdesktop_lowericon_z88_cart_1_accion,NULL,
		bitmap_lowericon_ext_desktop_z88_slot_one_active,bitmap_lowericon_ext_desktop_z88_slot_one_inactive,&zxdesktop_common_icon_no_inverse},

	{ zxdesktop_lowericon_z88_cart_is_visible, zxdesktop_lowericon_z88_cart_2_is_active, zxdesktop_lowericon_z88_cart_2_accion,NULL,
		bitmap_lowericon_ext_desktop_z88_slot_two_active,bitmap_lowericon_ext_desktop_z88_slot_two_inactive,&zxdesktop_common_icon_no_inverse},

	{ zxdesktop_lowericon_z88_cart_is_visible, zxdesktop_lowericon_z88_cart_3_is_active, zxdesktop_lowericon_z88_cart_3_accion,NULL,
		bitmap_lowericon_ext_desktop_z88_slot_three_active,bitmap_lowericon_ext_desktop_z88_slot_three_inactive,&z88_slot3_activity_indicator},

    //zxuno flash. En este caso no hay distincion entre icono insertado y no insertado
	{ zxdesktop_lowericon_zxunoflash_is_visible, zxdesktop_lowericon_zxunoflash_is_active, zxdesktop_lowericon_zxunoflash_accion,NULL,
		bitmap_lowericon_ext_desktop_zxunoflash,bitmap_lowericon_ext_desktop_zxunoflash,&zxdesktop_icon_zxunoflash_inverse},

    //zxmmc+ flash. En este caso no hay distincion entre icono insertado y no insertado
	{ zxdesktop_lowericon_zxmmcplusflash_is_visible, zxdesktop_lowericon_zxmmcplusflash_is_active, zxdesktop_lowericon_zxmmcplusflash_accion,NULL,
		bitmap_lowericon_ext_desktop_zxmmcplusflash,bitmap_lowericon_ext_desktop_zxmmcplusflash,&zxdesktop_icon_zxmmcplusflash_inverse},

    //cartucho dandanator
	{ zxdesktop_lowericon_dandanator_is_visible, zxdesktop_lowericon_dandanator_is_active, zxdesktop_lowericon_dandanator_accion,
        zxdesktop_lowericon_dandanator_accion_boton_derecho,
		bitmap_lowericon_ext_desktop_dandanator_active,bitmap_lowericon_ext_desktop_dandanator_inactive,&zxdesktop_icon_dandanator_inverse},

    //cinta HiLow
	{ zxdesktop_lowericon_hilow_is_visible, zxdesktop_lowericon_hilow_is_active,zxdesktop_lowericon_hilow_accion,
        zxdesktop_lowericon_hilow_accion_boton_derecho,
		bitmap_lowericon_ext_desktop_hilow_active,bitmap_lowericon_ext_desktop_hilow_inactive,&zxdesktop_icon_hilow_inverse},

};





//
// Fin funciones de dispositivos especificos en el ZX Desktop
//


/*
Inicio de Template de ventana de menu que se puede enviar a background
Sustituir "template_window_can_be_backgrounded" por el nombre de la ventana
Sustituir "templatewindowname" por el nombre corto de la ventana (nombre identificativo de geometria, string sin _)
Sustituir "Window title" por el titulo de la ventana
Y definirla en zxvision_known_window_names_array
*/


zxvision_window *menu_template_window_can_be_backgrounded_window;


void menu_template_window_can_be_backgrounded_overlay(void)
{

    menu_speech_set_tecla_pulsada(); //Si no, envia continuamente todo ese texto a speech

    //si ventana minimizada, no ejecutar todo el codigo de overlay
    if (menu_template_window_can_be_backgrounded_window->is_minimized) return;


    //Print....
    //Tambien contar si se escribe siempre o se tiene en cuenta contador_segundo...


    //Mostrar contenido
    zxvision_draw_window_contents(menu_template_window_can_be_backgrounded_window);

}




//Almacenar la estructura de ventana aqui para que se pueda referenciar desde otros sitios
zxvision_window zxvision_window_template_window_can_be_backgrounded;


void menu_template_window_can_be_backgrounded(MENU_ITEM_PARAMETERS)
{
	menu_espera_no_tecla();

    if (!menu_multitarea) {
        menu_warn_message("This window needs multitask enabled");
        return;
    }

    zxvision_window *ventana;
    ventana=&zxvision_window_template_window_can_be_backgrounded;

	//IMPORTANTE! no crear ventana si ya existe. Esto hay que hacerlo en todas las ventanas que permiten background.
	//si no se hiciera, se crearia la misma ventana, y en la lista de ventanas activas , al redibujarse,
	//la primera ventana repetida apuntaria a la segunda, que es el mismo puntero, y redibujaria la misma, y se quedaria en bucle colgado
	//zxvision_delete_window_if_exists(ventana);

    //Crear ventana si no existe
    if (!zxvision_if_window_already_exists(ventana)) {
        int xventana,yventana,ancho_ventana,alto_ventana,is_minimized,is_maximized,ancho_antes_minimize,alto_antes_minimize;

        if (!util_find_window_geometry("templatewindowname",&xventana,&yventana,&ancho_ventana,&alto_ventana,&is_minimized,&is_maximized,&ancho_antes_minimize,&alto_antes_minimize)) {
            ancho_ventana=30;
            alto_ventana=20;

            xventana=menu_center_x()-ancho_ventana/2;
            yventana=menu_center_y()-alto_ventana/2;
        }


        zxvision_new_window_gn_cim(ventana,xventana,yventana,ancho_ventana,alto_ventana,ancho_ventana-1,alto_ventana-2,"Window title",
            "templatewindowname",is_minimized,is_maximized,ancho_antes_minimize,alto_antes_minimize);

        ventana->can_be_backgrounded=1;

    }

    //Si ya existe, activar esta ventana
    else {
        zxvision_activate_this_window(ventana);
    }

	zxvision_draw_window(ventana);

	z80_byte tecla;


	int salir=0;


    menu_template_window_can_be_backgrounded_window=ventana; //Decimos que el overlay lo hace sobre la ventana que tenemos aqui


    //cambio overlay
    zxvision_set_window_overlay(ventana,menu_template_window_can_be_backgrounded_overlay);


    //Toda ventana que este listada en zxvision_known_window_names_array debe permitir poder salir desde aqui
    //Se sale despues de haber inicializado overlay y de cualquier otra variable que necesite el overlay
    if (zxvision_currently_restoring_windows_on_start) {
        //printf ("Saliendo de ventana ya que la estamos restaurando en startup\n");
        return;
    }

    do {


		tecla=zxvision_common_getkey_refresh();


        switch (tecla) {

            case 11:
                //arriba
                //blablabla
            break;



            //Salir con ESC
            case 2:
                salir=1;
            break;

            //O tecla background
            case 3:
                salir=1;
            break;
        }


    } while (salir==0);


	util_add_window_geometry_compact(ventana);

	if (tecla==3) {
		zxvision_message_put_window_background();
	}

	else {
		zxvision_destroy_window(ventana);
	}


}



/*
Fin de Template de ventana de menu que se puede enviar a background
*/



/*

Inicio de template para gestion de menu

Sustituir:

template_menu por el nombre del menu (aparece en la funcion y en la variable de opcion seleccionada)
Cambiar titulo de menu en llamada a funcion menu_dibuja_menu
*/

void menu_template_menu(MENU_ITEM_PARAMETERS)
{
    menu_item *array_menu_common;
    menu_item item_seleccionado;
    int retorno_menu;


    do {

        menu_add_item_menu_inicial_format(&array_menu_common,MENU_OPCION_NORMAL,menu_defcon,NULL,"~~Defcon");
        menu_add_item_menu_shortcut(array_menu_common,'d');
        menu_add_item_menu_tiene_submenu(array_menu_common);

        menu_add_item_menu_format(array_menu_common,MENU_OPCION_NORMAL,menu_dinamid3,NULL,"D~~inamid3");
        menu_add_item_menu_shortcut(array_menu_common,'i');
        menu_add_item_menu_tiene_submenu(array_menu_common);

        menu_add_item_menu_format(array_menu_common,MENU_OPCION_NORMAL,menu_hilow_barbanegra,NULL,"HiLow ~~Barbanegra");
        menu_add_item_menu_shortcut(array_menu_common,'b');
        menu_add_item_menu_tiene_submenu(array_menu_common);

        menu_add_item_menu_format(array_menu_common,MENU_OPCION_NORMAL,menu_interface007,NULL,"I~~nterface007");
        menu_add_item_menu_shortcut(array_menu_common,'n');
        menu_add_item_menu_tiene_submenu(array_menu_common);

        if (MACHINE_IS_SPECTRUM_16_48) {
            menu_add_item_menu_format(array_menu_common,MENU_OPCION_NORMAL,menu_mantransfer,NULL,"M~~antransfer");
            menu_add_item_menu_shortcut(array_menu_common,'a');
            menu_add_item_menu_tiene_submenu(array_menu_common);
        }

        menu_add_item_menu_format(array_menu_common,MENU_OPCION_NORMAL,menu_mhpokeador,NULL,"Microhobby ~~Pokeador Automático");
        menu_add_item_menu_shortcut(array_menu_common,'p');
        menu_add_item_menu_tiene_submenu(array_menu_common);

        menu_add_item_menu_format(array_menu_common,MENU_OPCION_NORMAL,menu_multiface,NULL,"~~Multiface");
        menu_add_item_menu_shortcut(array_menu_common,'m');
        menu_add_item_menu_tiene_submenu(array_menu_common);

        menu_add_item_menu_format(array_menu_common,MENU_OPCION_NORMAL,menu_phoenix,NULL,"P~~hoenix");
        menu_add_item_menu_shortcut(array_menu_common,'h');
        menu_add_item_menu_tiene_submenu(array_menu_common);

        menu_add_item_menu_format(array_menu_common,MENU_OPCION_NORMAL,menu_ramjet,NULL,"~~Ramjet");
        menu_add_item_menu_shortcut(array_menu_common,'r');
        menu_add_item_menu_tiene_submenu(array_menu_common);

        menu_add_item_menu_format(array_menu_common,MENU_OPCION_NORMAL,menu_specmate,NULL,"~~Spec-Mate");
        menu_add_item_menu_shortcut(array_menu_common,'s');
        menu_add_item_menu_tiene_submenu(array_menu_common);

        menu_add_item_menu_format(array_menu_common,MENU_OPCION_NORMAL,menu_transtape,NULL,"~~Transtape");
        menu_add_item_menu_shortcut(array_menu_common,'t');
        menu_add_item_menu_tiene_submenu(array_menu_common);



        menu_add_item_menu_separator(array_menu_common);

        menu_add_ESC_item(array_menu_common);


        //Nota: si no se agrega el nombre del path del indice, se generará uno automáticamente
        menu_add_item_menu_index_full_path(array_menu_common,
            "Main Menu-> Storage-> Template","Menú Principal-> Almacenamiento-> Plantilla","Menú Principal-> Emmagatzematge-> Plantilla");

        retorno_menu=menu_dibuja_menu(&template_menu_opcion_seleccionada,&item_seleccionado,array_menu_common,
            "Template Menu","Menú Plantilla","Menú Plantilla" );

        if ((item_seleccionado.tipo_opcion&MENU_OPCION_ESC)==0 && retorno_menu>=0) {
            //llamamos por valor de funcion
            if (item_seleccionado.menu_funcion!=NULL) {
                //printf ("actuamos por funcion\n");
                item_seleccionado.menu_funcion(item_seleccionado.valor_opcion);

            }
        }

    } while ( (item_seleccionado.tipo_opcion&MENU_OPCION_ESC)==0 && retorno_menu!=MENU_RETORNO_ESC && !salir_todos_menus);

}


/*

Fin de template para gestion de menu

*/