diff --git a/Makefile.common b/Makefile.common index a7a3547..c3a66ee 100644 --- a/Makefile.common +++ b/Makefile.common @@ -19,6 +19,7 @@ SOURCES_C := \ $(LIBRETRO_COMM_DIR)/encodings/encoding_utf.c \ $(LIBRETRO_COMM_DIR)/file/file_path.c \ $(LIBRETRO_COMM_DIR)/file/file_path_io.c \ + $(LIBRETRO_COMM_DIR)/streams/memory_stream.c \ $(LIBRETRO_COMM_DIR)/string/stdstring.c \ $(LIBRETRO_COMM_DIR)/time/rtime.c \ $(LIBRETRO_COMM_DIR)/vfs/vfs_implementation.c @@ -27,7 +28,10 @@ SOURCES_C += \ $(CORE_DIR)/libretro/libretro-core.c \ $(CORE_DIR)/libretro/core-mapper.c \ $(CORE_DIR)/libretro/graph.c \ - $(CORE_DIR)/libretro/vkbd.c + $(CORE_DIR)/libretro/vkbd.c \ + $(CORE_DIR)/libretro/retro_strings.c \ + $(CORE_DIR)/libretro/retro_utils.c \ + $(CORE_DIR)/libretro/retro_disk_control.c SOURCES_C += \ $(CORE_DIR)/atari800/src/afile.c \ diff --git a/atari800/src/android/jni/jni.c b/atari800/src/android/jni/jni.c index 01aac59..134b1ab 100644 --- a/atari800/src/android/jni/jni.c +++ b/atari800/src/android/jni/jni.c @@ -254,7 +254,12 @@ static jint JNICALL NativeRunAtariProgram(JNIEnv *env, jobject this, CARTRIDGE_MEGA_2048_DESC, CARTRIDGE_THECART_32M_DESC, CARTRIDGE_THECART_64M_DESC, - CARTRIDGE_XEGS_8F_64_DESC + CARTRIDGE_XEGS_8F_64_DESC, + CARTRIDGE_5200_SUPER_64_DESC, + CARTRIDGE_5200_SUPER_128_DESC, + CARTRIDGE_5200_SUPER_256_DESC, + CARTRIDGE_5200_SUPER_512_DESC, + CARTRIDGE_ATMAX_NEW_1024_DESC }; const jbyte *img_utf = NULL; diff --git a/atari800/src/antic.c b/atari800/src/antic.c index b24c473..e6084bd 100644 --- a/atari800/src/antic.c +++ b/atari800/src/antic.c @@ -4085,6 +4085,63 @@ case we have ANTIC_cpu2antic_ptr[ANTIC_WSYNC_C+1]-1 = 8 and in the 2nd =12 */ #ifndef BASIC +#if defined(__LIBRETRO__) +void Retro_ANTIC_StateSave(void) +{ + Retro_SaveUBYTE(&ANTIC_DMACTL, 1); + Retro_SaveUBYTE(&ANTIC_CHACTL, 1); + Retro_SaveUBYTE(&ANTIC_HSCROL, 1); + Retro_SaveUBYTE(&ANTIC_VSCROL, 1); + Retro_SaveUBYTE(&ANTIC_PMBASE, 1); + Retro_SaveUBYTE(&ANTIC_CHBASE, 1); + Retro_SaveUBYTE(&ANTIC_NMIEN, 1); + Retro_SaveUBYTE(&ANTIC_NMIST, 1); + Retro_SaveUBYTE(&IR, 1); + Retro_SaveUBYTE(&anticmode, 1); + Retro_SaveUBYTE(&dctr, 1); + Retro_SaveUBYTE(&lastline, 1); + Retro_SaveUBYTE(&need_dl, 1); + Retro_SaveUBYTE(&vscrol_off, 1); + + Retro_SaveUWORD(&ANTIC_dlist, 1); + Retro_SaveUWORD(&screenaddr, 1); + + Retro_SaveINT(&ANTIC_xpos, 1); + Retro_SaveINT(&ANTIC_xpos_limit, 1); + Retro_SaveINT(&ANTIC_ypos, 1); +} + +void Retro_ANTIC_StateRead(void) +{ + Retro_ReadUBYTE(&ANTIC_DMACTL, 1); + Retro_ReadUBYTE(&ANTIC_CHACTL, 1); + Retro_ReadUBYTE(&ANTIC_HSCROL, 1); + Retro_ReadUBYTE(&ANTIC_VSCROL, 1); + Retro_ReadUBYTE(&ANTIC_PMBASE, 1); + Retro_ReadUBYTE(&ANTIC_CHBASE, 1); + Retro_ReadUBYTE(&ANTIC_NMIEN, 1); + Retro_ReadUBYTE(&ANTIC_NMIST, 1); + Retro_ReadUBYTE(&IR, 1); + Retro_ReadUBYTE(&anticmode, 1); + Retro_ReadUBYTE(&dctr, 1); + Retro_ReadUBYTE(&lastline, 1); + Retro_ReadUBYTE(&need_dl, 1); + Retro_ReadUBYTE(&vscrol_off, 1); + + Retro_ReadUWORD(&ANTIC_dlist, 1); + Retro_ReadUWORD(&screenaddr, 1); + + Retro_ReadINT(&ANTIC_xpos, 1); + Retro_ReadINT(&ANTIC_xpos_limit, 1); + Retro_ReadINT(&ANTIC_ypos, 1); + + ANTIC_PutByte(ANTIC_OFFSET_DMACTL, ANTIC_DMACTL); + ANTIC_PutByte(ANTIC_OFFSET_CHACTL, ANTIC_CHACTL); + ANTIC_PutByte(ANTIC_OFFSET_PMBASE, ANTIC_PMBASE); + ANTIC_PutByte(ANTIC_OFFSET_CHBASE, ANTIC_CHBASE); +} +#endif /* __LIBRETRO__ */ + void ANTIC_StateSave(void) { StateSav_SaveUBYTE(&ANTIC_DMACTL, 1); diff --git a/atari800/src/antic.h b/atari800/src/antic.h index 769bde0..8102378 100644 --- a/atari800/src/antic.h +++ b/atari800/src/antic.h @@ -94,6 +94,11 @@ void ANTIC_SetPrior(UBYTE prior); void ANTIC_StateSave(void); void ANTIC_StateRead(void); +#if defined(__LIBRETRO__) +void Retro_ANTIC_StateSave(void); +void Retro_ANTIC_StateRead(void); +#endif + /* Pointer to 16 KB seen by ANTIC in 0x4000-0x7fff. If it's the same what the CPU sees (and what's in memory[0x4000..0x7fff], then NULL. */ diff --git a/atari800/src/atari.c b/atari800/src/atari.c index d099618..8c4e450 100644 --- a/atari800/src/atari.c +++ b/atari800/src/atari.c @@ -147,7 +147,7 @@ #endif #if defined(__LIBRETRO__) extern const char *retro_system_directory; -#endif +#endif /* __LIBRETRO__ */ int Atari800_machine_type = Atari800_MACHINE_XLXE; @@ -1377,6 +1377,131 @@ void Atari800_Frame(void) #ifndef BASIC +#if defined(__LIBRETRO__) +void Retro_Atari800_StateSave(void) +{ + UBYTE temp = Atari800_tv_mode == Atari800_TV_PAL; + Retro_SaveUBYTE(&temp, 1); + temp = Atari800_machine_type; + Retro_SaveUBYTE(&temp, 1); + if (Atari800_machine_type == Atari800_MACHINE_XLXE) { + temp = Atari800_builtin_basic; + Retro_SaveUBYTE(&temp, 1); + temp = Atari800_keyboard_leds; + Retro_SaveUBYTE(&temp, 1); + temp = Atari800_f_keys; + Retro_SaveUBYTE(&temp, 1); + temp = Atari800_jumper; + Retro_SaveUBYTE(&temp, 1); + temp = Atari800_builtin_game; + Retro_SaveUBYTE(&temp, 1); + temp = Atari800_keyboard_detached; + Retro_SaveUBYTE(&temp, 1); + } +} + +void Retro_Atari800_StateRead(UBYTE version) +{ + if (version >= 7) { + UBYTE temp; + Retro_ReadUBYTE(&temp, 1); + Atari800_SetTVMode(temp ? Atari800_TV_PAL : Atari800_TV_NTSC); + Retro_ReadUBYTE(&temp, 1); + if (temp < 0 || temp >= Atari800_MACHINE_SIZE) { + temp = Atari800_MACHINE_XLXE; + Log_print("Warning: Bad machine type read in from state save, defaulting to XL/XE"); + } + Atari800_SetMachineType(temp); + if (Atari800_machine_type == Atari800_MACHINE_XLXE) { + Retro_ReadUBYTE(&temp, 1); + Atari800_builtin_basic = temp != 0; + Retro_ReadUBYTE(&temp, 1); + Atari800_keyboard_leds = temp != 0; + Retro_ReadUBYTE(&temp, 1); + Atari800_f_keys = temp != 0; + Retro_ReadUBYTE(&temp, 1); + Atari800_jumper = temp != 0; + Atari800_UpdateJumper(); + Retro_ReadUBYTE(&temp, 1); + Atari800_builtin_game = temp != 0; + Retro_ReadUBYTE(&temp, 1); + Atari800_keyboard_detached = temp != 0; + Atari800_UpdateKeyboardDetached(); + } + } + else { /* savestate from version 2.2.1 or earlier */ + int new_tv_mode; + /* these are all for compatibility with previous versions */ + UBYTE temp; + int default_tv_mode; + int os; + int default_system; + int pil_on; + + Retro_ReadUBYTE(&temp, 1); + new_tv_mode = (temp == 0) ? Atari800_TV_PAL : Atari800_TV_NTSC; + Atari800_SetTVMode(new_tv_mode); + + Retro_ReadUBYTE(&temp, 1); + Retro_ReadINT(&os, 1); + switch (temp) { + case 0: + Atari800_machine_type = Atari800_MACHINE_800; + MEMORY_ram_size = 48; + break; + case 1: + Atari800_machine_type = Atari800_MACHINE_XLXE; + MEMORY_ram_size = 64; + break; + case 2: + Atari800_machine_type = Atari800_MACHINE_XLXE; + MEMORY_ram_size = 128; + break; + case 3: + Atari800_machine_type = Atari800_MACHINE_XLXE; + MEMORY_ram_size = MEMORY_RAM_320_COMPY_SHOP; + break; + case 4: + Atari800_machine_type = Atari800_MACHINE_5200; + MEMORY_ram_size = 16; + break; + case 5: + Atari800_machine_type = Atari800_MACHINE_800; + MEMORY_ram_size = 16; + break; + case 6: + Atari800_machine_type = Atari800_MACHINE_XLXE; + MEMORY_ram_size = 16; + break; + case 7: + Atari800_machine_type = Atari800_MACHINE_XLXE; + MEMORY_ram_size = 576; + break; + case 8: + Atari800_machine_type = Atari800_MACHINE_XLXE; + MEMORY_ram_size = 1088; + break; + case 9: + Atari800_machine_type = Atari800_MACHINE_XLXE; + MEMORY_ram_size = 192; + break; + default: + Atari800_machine_type = Atari800_MACHINE_XLXE; + MEMORY_ram_size = 64; + Log_print("Warning: Bad machine type read in from state save, defaulting to 800 XL"); + break; + } + + Retro_ReadINT(&pil_on, 1); + Retro_ReadINT(&default_tv_mode, 1); + Retro_ReadINT(&default_system, 1); + Atari800_SetMachineType(Atari800_machine_type); + } + load_roms(); + /* XXX: what about patches? */ +} +#endif /* __LIBRETRO__ */ + void Atari800_StateSave(void) { UBYTE temp = Atari800_tv_mode == Atari800_TV_PAL; diff --git a/atari800/src/atari.h b/atari800/src/atari.h index de52cba..b1b58c5 100644 --- a/atari800/src/atari.h +++ b/atari800/src/atari.h @@ -187,4 +187,10 @@ void Atari800_StateRead(UBYTE version); /* Change TV mode. */ void Atari800_SetTVMode(int mode); +#if defined(__LIBRETRO__) +/* Save State */ +void Retro_Atari800_StateSave(void); +void Retro_Atari800_StateRead(UBYTE version); +#endif + #endif /* ATARI_H_ */ diff --git a/atari800/src/cartridge.c b/atari800/src/cartridge.c index 390af97..8f30bec 100644 --- a/atari800/src/cartridge.c +++ b/atari800/src/cartridge.c @@ -119,7 +119,12 @@ int const CARTRIDGE_kb[CARTRIDGE_LAST_SUPPORTED + 1] = { 2048, /* CARTRIDGE_MEGA_2048 */ 32*1024, /* CARTRIDGE_THECART_32M */ 64*1024, /* CARTRIDGE_THECART_64M */ - 64 /* CARTRIDGE_XEGS_64_8F */ + 64, /* CARTRIDGE_XEGS_64_8F */ + 64, /* CARTRIDGE_5200_SUPER_64 */ + 128, /* CARTRIDGE_5200_SUPER_128 */ + 256, /* CARTRIDGE_5200_SUPER_256 */ + 512, /* CARTRIDGE_5200_SUPER_512 */ + 1024, /* CARTRIDGE_ATMAX_NEW_1024 */ }; int CARTRIDGE_autoreboot = TRUE; @@ -133,6 +138,10 @@ static int CartIsFor5200(int type) case CARTRIDGE_5200_NS_16: case CARTRIDGE_5200_8: case CARTRIDGE_5200_4: + case CARTRIDGE_5200_SUPER_64: + case CARTRIDGE_5200_SUPER_128: + case CARTRIDGE_5200_SUPER_256: + case CARTRIDGE_5200_SUPER_512: return TRUE; default: break; @@ -226,6 +235,11 @@ static void set_bank_80BF(void) } } +static void set_bank_5200_SUPER(void) +{ + MEMORY_CopyROM(0x4000, 0xbfff, active_cart->image + active_cart->state * 0x8000); +} + static void set_bank_SDX_128(void) { if (active_cart->state & 8) @@ -402,7 +416,20 @@ static void MapActiveCart(void) if (Atari800_machine_type == Atari800_MACHINE_5200) { MEMORY_SetROM(0x4ff6, 0x4ff9); /* disable Bounty Bob bank switching */ MEMORY_SetROM(0x5ff6, 0x5ff9); + MEMORY_SetROM(0xbfc0, 0xbfff); /* disable Super Cart bank switching */ switch (active_cart->type) { + case CARTRIDGE_5200_SUPER_64: + case CARTRIDGE_5200_SUPER_128: + case CARTRIDGE_5200_SUPER_256: + case CARTRIDGE_5200_SUPER_512: + set_bank_5200_SUPER(); +#ifndef PAGED_ATTRIB + MEMORY_SetHARDWARE(0xbfc0, 0xbfff); +#else + MEMORY_readmap[0xbf] = CARTRIDGE_5200SuperCartGetByte; + MEMORY_writemap[0xbf] = CARTRIDGE_5200SuperCartPutByte; +#endif + break; case CARTRIDGE_5200_32: MEMORY_CopyROM(0x4000, 0xbfff, active_cart->image); break; @@ -1151,12 +1178,10 @@ UBYTE CARTRIDGE_BountyBob2GetByte(UWORD addr, int no_side_effects) if (Atari800_machine_type == Atari800_MACHINE_5200) { if (addr >= 0x5ff6 && addr <= 0x5ff9) { CARTRIDGE_BountyBob2(addr); - return 0; } } else { if (addr >= 0x9ff6 && addr <= 0x9ff9) { CARTRIDGE_BountyBob2(addr); - return 0; } } } @@ -1200,6 +1225,45 @@ int CARTRIDGE_Checksum(const UBYTE *image, int nbytes) return checksum; } +/* addr must be $bfxx in 5200 mode only. */ +static void access_5200SuperCart(UWORD addr) +{ + int old_state = active_cart->state; + int new_state = old_state; + + if ((addr & 0xc0) == 0xc0) { + switch (addr & 0x30) { + case 0x00: /* $BFCx */ + new_state = (new_state & 0x03) | (addr & 0x0c); + break; + case 0x10: /* $BFDx */ + new_state = (new_state & 0x0c) | ((addr & 0x0c) >> 2); + break; + default: /* 0x20 or 0x30, i.e. $BFEx or $BFFx */ + new_state = 0x0f; + break; + } + new_state &= ((active_cart->size >> 5) - 1); + } + + if (old_state != new_state) { + active_cart->state = new_state; + set_bank_5200_SUPER(); + } +} + +UBYTE CARTRIDGE_5200SuperCartGetByte(UWORD addr, int no_side_effects) +{ + if (!no_side_effects) + access_5200SuperCart(addr); + return MEMORY_dGetByte(addr); +} + +void CARTRIDGE_5200SuperCartPutByte(UWORD addr, UBYTE value) +{ + access_5200SuperCart(addr); +} + static void ResetCartState(CARTRIDGE_image_t *cart) { switch (cart->type) { @@ -1371,10 +1435,16 @@ void CARTRIDGE_ColdStart(void) { ResetCartState(&CARTRIDGE_piggyback); MapActiveCart(); } + #ifdef __LIBRETRO__ #include "atari5200_hash.h" -extern int autorun5200; -#endif +#include "atari800_hash.h" +#include "esc.h" +#include "pokeysnd.h" +extern int autorunCartridge; +extern void retro_message(const char* text, unsigned int frames, int alt); +#endif /* __LIBRETRO __ */ + /* Loads a cartridge from FILENAME. Copies FILENAME to CART_FILENAME. Allocates a buffer with cartridge image data and puts it in *CART_IMAGE. Sets *CART_TYPE to the cartridge type. */ @@ -1385,7 +1455,8 @@ static int InsertCartridge(const char *filename, CARTRIDGE_image_t *cart) int type; UBYTE header[16]; #ifdef __LIBRETRO__ - ULONG crc; + ULONG crc; + static int RetroMsgShown = FALSE; #endif /* open file */ fp = fopen(filename, "rb"); @@ -1395,7 +1466,7 @@ static int InsertCartridge(const char *filename, CARTRIDGE_image_t *cart) len = Util_flen(fp); Util_rewind(fp); #ifdef __LIBRETRO__ - if(autorun5200){ + if(autorunCartridge){ CRC32_FromFile(fp, &crc); Util_rewind(fp); } @@ -1408,7 +1479,7 @@ static int InsertCartridge(const char *filename, CARTRIDGE_image_t *cart) /* if full kilobytes, assume it is raw image */ if ((len & 0x3ff) == 0) { /* alloc memory and read data */ - cart->image = (UBYTE *) Util_malloc(len); + cart->image = (UBYTE*)Util_malloc(len); if (fread(cart->image, 1, len, fp) < len) { Log_print("Error reading cartridge.\n"); } @@ -1418,40 +1489,120 @@ static int InsertCartridge(const char *filename, CARTRIDGE_image_t *cart) len >>= 10; /* number of kilobytes */ cart->size = len; #ifdef __LIBRETRO__ - if(autorun5200){ - int match=0,i=0; - printf("Hack Libretro:crc A5200 ON sz:%d crc:%x\n",cart->size,crc); - while(a5200_game[i].type!=-1){ - if(crc==a5200_game[i].crc){ - match=1; - if(a5200_game[i].type==0) - switch(cart->size*1024){ - case 4096: - cart->type =CARTRIDGE_5200_4; - break; - case 8192: - cart->type =CARTRIDGE_5200_8; - break; - case 16384: - cart->type =CARTRIDGE_5200_NS_16; - break; - case 32768: - cart->type =CARTRIDGE_5200_32; - break; + if (autorunCartridge == 1) { + int match = 0, i = 0; + printf("Hack Libretro:crc A5200 ON sz:%d crc:%x\n", cart->size, crc); + while (a5200_game[i].type != -1) { + if (crc == a5200_game[i].crc) { + match = 1; + if (a5200_game[i].type == 0) + switch (cart->size * 1024) { + case 4096: + cart->type = CARTRIDGE_5200_4; + break; + case 8192: + cart->type = CARTRIDGE_5200_8; + break; + case 16384: + cart->type = CARTRIDGE_5200_NS_16; + break; + case 32768: + cart->type = CARTRIDGE_5200_32; + break; + } + else if (a5200_game[i].type == a5200_40) { + /* Bounty Bob don't like stereo pokey (game locks) */ + cart->type = CARTRIDGE_5200_40; + POKEYSND_stereo_enabled = FALSE; + } + else if (a5200_game[i].type == a5200_ee_16) + cart->type = CARTRIDGE_5200_EE_16; + else if (a5200_game[i].type == a5200_64) + cart->type = CARTRIDGE_5200_SUPER_64; + else if (a5200_game[i].type == a5200_128) + cart->type = CARTRIDGE_5200_SUPER_128; // I've yet to see this type + else if (a5200_game[i].type == a5200_256) + cart->type = CARTRIDGE_5200_SUPER_256; // I've yet to see this type + else if (a5200_game[i].type == a5200_512) + cart->type = CARTRIDGE_5200_SUPER_512; + + printf("Hack Libretro:A5200 cart->type:%d %x\n", cart->type, crc); + break; + } + i++; + } + + if (match == 1) { + if (!RetroMsgShown) + retro_message("5200 Cart found in DB.", 1000, 0); + goto label_fin; + } + } + else if (autorunCartridge == 2) { + int match = 0, i = 0; + printf("Hack Libretro:crc A800 ON sz:%d crc:%x\n", cart->size, crc); + while (a800_game[i].type != -1) { + if (crc == a800_game[i].crc) { + match = 1; + if (a800_game[i].type == 0) + switch (cart->size * 1024) { + case 8192: + cart->type = CARTRIDGE_STD_8; + break; + case 16384: + cart->type = CARTRIDGE_STD_16; + break; + case 32768: + cart->type = CARTRIDGE_XEGS_32; + break; } - else if(a5200_game[i].type==1)cart->type =CARTRIDGE_5200_40; - else if(a5200_game[i].type==2)cart->type =CARTRIDGE_5200_EE_16; - printf("Hack Libretro:A5200 cart->type:%d %x\n",cart->type,crc); + else if (a800_game[i].type == a800_40) { + /* Bounty Bob don't like stereo pokey (game locks) */ + cart->type = CARTRIDGE_BBSB_40; + POKEYSND_stereo_enabled = FALSE; + } + else if (a800_game[i].type == a800_WILL_64) + cart->type = CARTRIDGE_WILL_64; + else if (a800_game[i].type == a800_XE_07_64) + cart->type = CARTRIDGE_XEGS_07_64; + else if (a800_game[i].type == a800_XE_128) + cart->type = CARTRIDGE_XEGS_128; + else if (a800_game[i].type == a800_MAX_128) { + /* Some ATMAX 1024 carts dislike Hi Speed SIO*/ + match = 2; + cart->type = CARTRIDGE_ATMAX_128; + } + else if (a800_game[i].type == a800_MAX_1024) { + /* Some ATMAX 1024 carts dislike Hi Speed SIO*/ + match = 2; + cart->type = CARTRIDGE_ATMAX_1024; + } + + printf("Hack Libretro:A800 cart->type:%d %x\n", cart->type, crc); break; } - i++; } - if(match==1)goto label_fin; - } -#endif + if (match == 1) { + if (!RetroMsgShown) + retro_message("800 Cart found in DB.", 1000, 0); + + goto label_fin; + } + else if (match == 2) { + if (!RetroMsgShown) + retro_message("800 Cart found in DB. Some ATMAX carts need SIO Accleration disabled.", 1000, 0); + + goto label_fin; + } + else if (!RetroMsgShown) + retro_message("800 Cart NOT found in DB.", 6000, 0); + } + + RetroMsgShown = TRUE; +#endif /* __LIBRETRO__ */ for (type = 1; type <= CARTRIDGE_LAST_SUPPORTED; type++) if (CARTRIDGE_kb[type] == len) { if (cart->type == CARTRIDGE_NONE) { @@ -1465,6 +1616,8 @@ static int InsertCartridge(const char *filename, CARTRIDGE_image_t *cart) #ifdef __LIBRETRO__ label_fin: #endif + RetroMsgShown = TRUE; + if (cart->type != CARTRIDGE_NONE) { InitCartridge(cart); return 0; /* ok */ @@ -1712,6 +1865,30 @@ void CARTRIDGE_Exit(void) #ifndef BASIC +void CARTRIDGE_StateSave(void) +{ + int cart_save = CARTRIDGE_main.type; + + if (CARTRIDGE_piggyback.type != CARTRIDGE_NONE) + /* Save the cart type as negative, to indicate to CARTStateRead that there is a + second cartridge */ + cart_save = -cart_save; + + /* Save the cartridge type, or CARTRIDGE_NONE if there isn't one...*/ + StateSav_SaveINT(&cart_save, 1); + if (CARTRIDGE_main.type != CARTRIDGE_NONE) { + StateSav_SaveFNAME(CARTRIDGE_main.filename); + StateSav_SaveINT(&CARTRIDGE_main.state, 1); + } + + if (CARTRIDGE_piggyback.type != CARTRIDGE_NONE) { + /* Save the second cartridge type and name*/ + StateSav_SaveINT(&CARTRIDGE_piggyback.type, 1); + StateSav_SaveFNAME(CARTRIDGE_piggyback.filename); + StateSav_SaveINT(&CARTRIDGE_piggyback.state, 1); + } +} + void CARTRIDGE_StateRead(UBYTE version) { int saved_type = CARTRIDGE_NONE; @@ -1777,30 +1954,97 @@ void CARTRIDGE_StateRead(UBYTE version) MapActiveCart(); } -void CARTRIDGE_StateSave(void) +#if defined(__LIBRETRO__) +void Retro_CARTRIDGE_StateSave(void) { int cart_save = CARTRIDGE_main.type; - + if (CARTRIDGE_piggyback.type != CARTRIDGE_NONE) - /* Save the cart type as negative, to indicate to CARTStateRead that there is a + /* Save the cart type as negative, to indicate to CARTStateRead that there is a second cartridge */ cart_save = -cart_save; - + /* Save the cartridge type, or CARTRIDGE_NONE if there isn't one...*/ - StateSav_SaveINT(&cart_save, 1); + Retro_SaveINT(&cart_save, 1); if (CARTRIDGE_main.type != CARTRIDGE_NONE) { - StateSav_SaveFNAME(CARTRIDGE_main.filename); - StateSav_SaveINT(&CARTRIDGE_main.state, 1); + Retro_SaveFNAME(CARTRIDGE_main.filename); + Retro_SaveINT(&CARTRIDGE_main.state, 1); } if (CARTRIDGE_piggyback.type != CARTRIDGE_NONE) { /* Save the second cartridge type and name*/ - StateSav_SaveINT(&CARTRIDGE_piggyback.type, 1); - StateSav_SaveFNAME(CARTRIDGE_piggyback.filename); - StateSav_SaveINT(&CARTRIDGE_piggyback.state, 1); + Retro_SaveINT(&CARTRIDGE_piggyback.type, 1); + Retro_SaveFNAME(CARTRIDGE_piggyback.filename); + Retro_SaveINT(&CARTRIDGE_piggyback.state, 1); } } +void Retro_CARTRIDGE_StateRead(UBYTE version) +{ + int saved_type = CARTRIDGE_NONE; + char filename[FILENAME_MAX]; + + /* Read the cart type from the file. If there is no cart type, becaused we have + reached the end of the file, this will just default to CART_NONE */ + Retro_ReadINT(&saved_type, 1); + if (saved_type != CARTRIDGE_NONE) { + Retro_ReadFNAME(filename); + if (filename[0]) { + /* Insert the cartridge... */ + if (CARTRIDGE_Insert(filename) >= 0) { + /* And set the type to the saved type, in case it was a raw cartridge image */ + CARTRIDGE_main.type = saved_type; + } + } + if (version >= 7) + /* Read the cartridge's state (current bank etc.). */ + Retro_ReadINT(&CARTRIDGE_main.state, 1); + } + else + CARTRIDGE_main.type = saved_type; + + if (saved_type < 0) { + /* Minus value indicates a piggyback cartridge present. */ + CARTRIDGE_main.type = -saved_type; + + Retro_ReadINT(&saved_type, 1); + Retro_ReadFNAME(filename); + if (filename[0]) { + /* Insert the cartridge... */ + if (CARTRIDGE_Insert_Second(filename) >= 0) { + /* And set the type to the saved type, in case it was a raw cartridge image */ + CARTRIDGE_piggyback.type = saved_type; + } + } + if (version >= 7) + /* Read the cartridge's state (current bank etc.). */ + Retro_ReadINT(&CARTRIDGE_piggyback.state, 1); + else { + /* Savestate version 6 explicitely stored information about + the active cartridge. */ + int piggyback_active; + Retro_ReadINT(&piggyback_active, 1); + if (piggyback_active) + active_cart = &CARTRIDGE_piggyback; + else + active_cart = &CARTRIDGE_main; + /* The "Determine active cartridge" code below makes no + sense when loading ver.6 savestates, because they + did not store the cartridge state. */ + return; + } + } + + /* Determine active cartridge (main or piggyback. */ + if (CartIsPassthrough(CARTRIDGE_main.type) && (CARTRIDGE_main.state & 0x0c) == 0x08) + active_cart = &CARTRIDGE_piggyback; + else + active_cart = &CARTRIDGE_main; + + MapActiveCart(); +} +#endif /* __LIBRETRO__ */ + #endif /* diff --git a/atari800/src/cartridge.h b/atari800/src/cartridge.h index 288814a..4028f46 100644 --- a/atari800/src/cartridge.h +++ b/atari800/src/cartridge.h @@ -74,7 +74,14 @@ enum { CARTRIDGE_THECART_32M = 65, CARTRIDGE_THECART_64M = 66, CARTRIDGE_XEGS_8F_64 = 67, - CARTRIDGE_LAST_SUPPORTED = 67 + + CARTRIDGE_5200_SUPER_64 = 68, // 71 + CARTRIDGE_5200_SUPER_128 = 69, // 72 + CARTRIDGE_5200_SUPER_256 = 70, // 73 + CARTRIDGE_5200_SUPER_512 = 71, // 74 + CARTRIDGE_ATMAX_NEW_1024 = 72, // 75 + + CARTRIDGE_LAST_SUPPORTED = 72 }; #define CARTRIDGE_MAX_SIZE (128 * 1024 * 1024) @@ -121,7 +128,7 @@ extern int const CARTRIDGE_kb[CARTRIDGE_LAST_SUPPORTED + 1]; #define CARTRIDGE_PHOENIX_8_DESC "Phoenix 8 KB cartridge" #define CARTRIDGE_BLIZZARD_16_DESC "Blizzard 16 KB cartridge" #define CARTRIDGE_ATMAX_128_DESC "Atarimax 128 KB Flash cartridge" -#define CARTRIDGE_ATMAX_1024_DESC "Atarimax 1 MB Flash cartridge" +#define CARTRIDGE_ATMAX_1024_DESC "Atarimax 1 MB Flash cartridge (old)" #define CARTRIDGE_SDX_128_DESC "SpartaDOS X 128 KB cartridge" #define CARTRIDGE_OSS_8_DESC "OSS 8 KB cartridge" #define CARTRIDGE_OSS_043M_16_DESC "OSS two chip 16 KB cartridge (043M)" @@ -147,6 +154,11 @@ extern int const CARTRIDGE_kb[CARTRIDGE_LAST_SUPPORTED + 1]; #define CARTRIDGE_THECART_32M_DESC "The!Cart 32 MB cartridge" #define CARTRIDGE_THECART_64M_DESC "The!Cart 64 MB cartridge" #define CARTRIDGE_XEGS_8F_64_DESC "XEGS 64 KB cartridge (banks 8-15)" +#define CARTRIDGE_5200_SUPER_64_DESC "64 KB Atari 5200 Super Cart" +#define CARTRIDGE_5200_SUPER_128_DESC "128 KB Atari 5200 Super Cart" +#define CARTRIDGE_5200_SUPER_256_DESC "256 KB Atari 5200 Super Cart" +#define CARTRIDGE_5200_SUPER_512_DESC "512 KB Atari 5200 Super Cart" +#define CARTRIDGE_ATMAX_NEW_1024_DESC "Atarimax 1 MB Flash cartridge" /* Indicates whether the emulator should automatically reboot (coldstart) after inserting/removing a cartridge. (Doesn't affect the piggyback @@ -203,6 +215,19 @@ void CARTRIDGE_BountyBob1(UWORD addr); void CARTRIDGE_BountyBob2(UWORD addr); void CARTRIDGE_StateSave(void); void CARTRIDGE_StateRead(UBYTE version); + +/* addr must be $bfxx in 5200 mode only. */ +UBYTE CARTRIDGE_5200SuperCartGetByte(UWORD addr, int no_side_effects); + +/* addr must be $bfxx in 5200 mode only. */ +void CARTRIDGE_5200SuperCartPutByte(UWORD addr, UBYTE value); + + +#if defined(__LIBRETRO__) +void Retro_CARTRIDGE_StateSave(void); +void Retro_CARTRIDGE_StateRead(UBYTE version); +#endif + #ifdef PAGED_ATTRIB UBYTE CARTRIDGE_BountyBob1GetByte(UWORD addr, int no_side_effects); UBYTE CARTRIDGE_BountyBob2GetByte(UWORD addr, int no_side_effects); diff --git a/atari800/src/cpu.c b/atari800/src/cpu.c index 2a1bdd8..88dfac1 100644 --- a/atari800/src/cpu.c +++ b/atari800/src/cpu.c @@ -2385,6 +2385,42 @@ void CPU_Reset(void) #if !defined(BASIC) && !defined(ASAP) +#if defined(__LIBRETRO__) +void Retro_CPU_StateSave(UBYTE SaveVerbose) +{ + Retro_SaveUBYTE(&CPU_regA, 1); + + CPU_GetStatus(); /* Make sure flags are all updated */ + Retro_SaveUBYTE(&CPU_regP, 1); + + Retro_SaveUBYTE(&CPU_regS, 1); + Retro_SaveUBYTE(&CPU_regX, 1); + Retro_SaveUBYTE(&CPU_regY, 1); + Retro_SaveUBYTE(&CPU_IRQ, 1); + + Retro_MEMORY_StateSave(SaveVerbose); + + Retro_SaveUWORD(&CPU_regPC, 1); +} + +void Retro_CPU_StateRead(UBYTE SaveVerbose, UBYTE StateVersion) +{ + Retro_ReadUBYTE(&CPU_regA, 1); + + Retro_ReadUBYTE(&CPU_regP, 1); + CPU_PutStatus(); /* Make sure flags are all updated */ + + Retro_ReadUBYTE(&CPU_regS, 1); + Retro_ReadUBYTE(&CPU_regX, 1); + Retro_ReadUBYTE(&CPU_regY, 1); + Retro_ReadUBYTE(&CPU_IRQ, 1); + + Retro_MEMORY_StateRead(SaveVerbose, StateVersion); + + Retro_ReadUWORD(&CPU_regPC, 1); +} +#endif /* __LIBRETRO__ */ + void CPU_StateSave(UBYTE SaveVerbose) { StateSav_SaveUBYTE(&CPU_regA, 1); diff --git a/atari800/src/cpu.h b/atari800/src/cpu.h index 5e64ca2..cab4e53 100644 --- a/atari800/src/cpu.h +++ b/atari800/src/cpu.h @@ -22,6 +22,12 @@ void CPU_PutStatus(void); void CPU_Reset(void); void CPU_StateSave(UBYTE SaveVerbose); void CPU_StateRead(UBYTE SaveVerbose, UBYTE StateVersion); + +#if defined(__LIBRETRO__) +void Retro_CPU_StateSave(UBYTE SaveVerbose); +void Retro_CPU_StateRead(UBYTE SaveVerbose, UBYTE StateVersion); +#endif + void CPU_NMI(void); void CPU_GO(int limit); #define CPU_GenerateIRQ() (CPU_IRQ = 1) diff --git a/atari800/src/gtia.c b/atari800/src/gtia.c index cc982f0..f14e5b9 100644 --- a/atari800/src/gtia.c +++ b/atari800/src/gtia.c @@ -1199,6 +1199,145 @@ void GTIA_PutByte(UWORD addr, UBYTE byte) #ifndef BASIC +#if defined(__LIBRETRO__) +void Retro_GTIA_StateSave(void) +{ + int next_console_value = 7; + + Retro_SaveUBYTE(>IA_HPOSP0, 1); + Retro_SaveUBYTE(>IA_HPOSP1, 1); + Retro_SaveUBYTE(>IA_HPOSP2, 1); + Retro_SaveUBYTE(>IA_HPOSP3, 1); + Retro_SaveUBYTE(>IA_HPOSM0, 1); + Retro_SaveUBYTE(>IA_HPOSM1, 1); + Retro_SaveUBYTE(>IA_HPOSM2, 1); + Retro_SaveUBYTE(>IA_HPOSM3, 1); + Retro_SaveUBYTE(&PF0PM, 1); + Retro_SaveUBYTE(&PF1PM, 1); + Retro_SaveUBYTE(&PF2PM, 1); + Retro_SaveUBYTE(&PF3PM, 1); + Retro_SaveUBYTE(>IA_M0PL, 1); + Retro_SaveUBYTE(>IA_M1PL, 1); + Retro_SaveUBYTE(>IA_M2PL, 1); + Retro_SaveUBYTE(>IA_M3PL, 1); + Retro_SaveUBYTE(>IA_P0PL, 1); + Retro_SaveUBYTE(>IA_P1PL, 1); + Retro_SaveUBYTE(>IA_P2PL, 1); + Retro_SaveUBYTE(>IA_P3PL, 1); + Retro_SaveUBYTE(>IA_SIZEP0, 1); + Retro_SaveUBYTE(>IA_SIZEP1, 1); + Retro_SaveUBYTE(>IA_SIZEP2, 1); + Retro_SaveUBYTE(>IA_SIZEP3, 1); + Retro_SaveUBYTE(>IA_SIZEM, 1); + Retro_SaveUBYTE(>IA_GRAFP0, 1); + Retro_SaveUBYTE(>IA_GRAFP1, 1); + Retro_SaveUBYTE(>IA_GRAFP2, 1); + Retro_SaveUBYTE(>IA_GRAFP3, 1); + Retro_SaveUBYTE(>IA_GRAFM, 1); + Retro_SaveUBYTE(>IA_COLPM0, 1); + Retro_SaveUBYTE(>IA_COLPM1, 1); + Retro_SaveUBYTE(>IA_COLPM2, 1); + Retro_SaveUBYTE(>IA_COLPM3, 1); + Retro_SaveUBYTE(>IA_COLPF0, 1); + Retro_SaveUBYTE(>IA_COLPF1, 1); + Retro_SaveUBYTE(>IA_COLPF2, 1); + Retro_SaveUBYTE(>IA_COLPF3, 1); + Retro_SaveUBYTE(>IA_COLBK, 1); + Retro_SaveUBYTE(>IA_PRIOR, 1); + Retro_SaveUBYTE(>IA_VDELAY, 1); + Retro_SaveUBYTE(>IA_GRACTL, 1); + + Retro_SaveUBYTE(&consol_mask, 1); + Retro_SaveINT(>IA_speaker, 1); + Retro_SaveINT(&next_console_value, 1); + Retro_SaveUBYTE(GTIA_TRIG_latch, 4); +} + +void Retro_GTIA_StateRead(UBYTE version) +{ + int next_console_value; /* ignored */ + + Retro_ReadUBYTE(>IA_HPOSP0, 1); + Retro_ReadUBYTE(>IA_HPOSP1, 1); + Retro_ReadUBYTE(>IA_HPOSP2, 1); + Retro_ReadUBYTE(>IA_HPOSP3, 1); + Retro_ReadUBYTE(>IA_HPOSM0, 1); + Retro_ReadUBYTE(>IA_HPOSM1, 1); + Retro_ReadUBYTE(>IA_HPOSM2, 1); + Retro_ReadUBYTE(>IA_HPOSM3, 1); + Retro_ReadUBYTE(&PF0PM, 1); + Retro_ReadUBYTE(&PF1PM, 1); + Retro_ReadUBYTE(&PF2PM, 1); + Retro_ReadUBYTE(&PF3PM, 1); + Retro_ReadUBYTE(>IA_M0PL, 1); + Retro_ReadUBYTE(>IA_M1PL, 1); + Retro_ReadUBYTE(>IA_M2PL, 1); + Retro_ReadUBYTE(>IA_M3PL, 1); + Retro_ReadUBYTE(>IA_P0PL, 1); + Retro_ReadUBYTE(>IA_P1PL, 1); + Retro_ReadUBYTE(>IA_P2PL, 1); + Retro_ReadUBYTE(>IA_P3PL, 1); + Retro_ReadUBYTE(>IA_SIZEP0, 1); + Retro_ReadUBYTE(>IA_SIZEP1, 1); + Retro_ReadUBYTE(>IA_SIZEP2, 1); + Retro_ReadUBYTE(>IA_SIZEP3, 1); + Retro_ReadUBYTE(>IA_SIZEM, 1); + Retro_ReadUBYTE(>IA_GRAFP0, 1); + Retro_ReadUBYTE(>IA_GRAFP1, 1); + Retro_ReadUBYTE(>IA_GRAFP2, 1); + Retro_ReadUBYTE(>IA_GRAFP3, 1); + Retro_ReadUBYTE(>IA_GRAFM, 1); + Retro_ReadUBYTE(>IA_COLPM0, 1); + Retro_ReadUBYTE(>IA_COLPM1, 1); + Retro_ReadUBYTE(>IA_COLPM2, 1); + Retro_ReadUBYTE(>IA_COLPM3, 1); + Retro_ReadUBYTE(>IA_COLPF0, 1); + Retro_ReadUBYTE(>IA_COLPF1, 1); + Retro_ReadUBYTE(>IA_COLPF2, 1); + Retro_ReadUBYTE(>IA_COLPF3, 1); + Retro_ReadUBYTE(>IA_COLBK, 1); + Retro_ReadUBYTE(>IA_PRIOR, 1); + Retro_ReadUBYTE(>IA_VDELAY, 1); + Retro_ReadUBYTE(>IA_GRACTL, 1); + + Retro_ReadUBYTE(&consol_mask, 1); + Retro_ReadINT(>IA_speaker, 1); + Retro_ReadINT(&next_console_value, 1); + if (version >= 7) + Retro_ReadUBYTE(GTIA_TRIG_latch, 4); + + GTIA_PutByte(GTIA_OFFSET_HPOSP0, GTIA_HPOSP0); + GTIA_PutByte(GTIA_OFFSET_HPOSP1, GTIA_HPOSP1); + GTIA_PutByte(GTIA_OFFSET_HPOSP2, GTIA_HPOSP2); + GTIA_PutByte(GTIA_OFFSET_HPOSP3, GTIA_HPOSP3); + GTIA_PutByte(GTIA_OFFSET_HPOSM0, GTIA_HPOSM0); + GTIA_PutByte(GTIA_OFFSET_HPOSM1, GTIA_HPOSM1); + GTIA_PutByte(GTIA_OFFSET_HPOSM2, GTIA_HPOSM2); + GTIA_PutByte(GTIA_OFFSET_HPOSM3, GTIA_HPOSM3); + GTIA_PutByte(GTIA_OFFSET_SIZEP0, GTIA_SIZEP0); + GTIA_PutByte(GTIA_OFFSET_SIZEP1, GTIA_SIZEP1); + GTIA_PutByte(GTIA_OFFSET_SIZEP2, GTIA_SIZEP2); + GTIA_PutByte(GTIA_OFFSET_SIZEP3, GTIA_SIZEP3); + GTIA_PutByte(GTIA_OFFSET_SIZEM, GTIA_SIZEM); + GTIA_PutByte(GTIA_OFFSET_GRAFP0, GTIA_GRAFP0); + GTIA_PutByte(GTIA_OFFSET_GRAFP1, GTIA_GRAFP1); + GTIA_PutByte(GTIA_OFFSET_GRAFP2, GTIA_GRAFP2); + GTIA_PutByte(GTIA_OFFSET_GRAFP3, GTIA_GRAFP3); + GTIA_PutByte(GTIA_OFFSET_GRAFM, GTIA_GRAFM); + GTIA_PutByte(GTIA_OFFSET_COLPM0, GTIA_COLPM0); + GTIA_PutByte(GTIA_OFFSET_COLPM1, GTIA_COLPM1); + GTIA_PutByte(GTIA_OFFSET_COLPM2, GTIA_COLPM2); + GTIA_PutByte(GTIA_OFFSET_COLPM3, GTIA_COLPM3); + GTIA_PutByte(GTIA_OFFSET_COLPF0, GTIA_COLPF0); + GTIA_PutByte(GTIA_OFFSET_COLPF1, GTIA_COLPF1); + GTIA_PutByte(GTIA_OFFSET_COLPF2, GTIA_COLPF2); + GTIA_PutByte(GTIA_OFFSET_COLPF3, GTIA_COLPF3); + GTIA_PutByte(GTIA_OFFSET_COLBK, GTIA_COLBK); + GTIA_PutByte(GTIA_OFFSET_PRIOR, GTIA_PRIOR); + GTIA_PutByte(GTIA_OFFSET_GRACTL, GTIA_GRACTL); +} +#endif /* __LIBRETRO__ */ + void GTIA_StateSave(void) { int next_console_value = 7; diff --git a/atari800/src/gtia.h b/atari800/src/gtia.h index 2a00f95..7f62849 100644 --- a/atari800/src/gtia.h +++ b/atari800/src/gtia.h @@ -132,6 +132,11 @@ void GTIA_PutByte(UWORD addr, UBYTE byte); void GTIA_StateSave(void); void GTIA_StateRead(UBYTE version); +#if defined(__LIBRETRO__) +void Retro_GTIA_StateSave(void); +void Retro_GTIA_StateRead(UBYTE version); +#endif + #ifdef NEW_CYCLE_EXACT void GTIA_UpdatePmplColls(void); #endif diff --git a/atari800/src/input.c b/atari800/src/input.c index 69a6edb..5fcf69e 100644 --- a/atari800/src/input.c +++ b/atari800/src/input.c @@ -48,7 +48,7 @@ #include #endif -#ifdef DREAMCAST +#if defined (DREAMCAST) || defined(__LIBRETRO__) extern int Atari_POT(int); #else #define Atari_POT(x) 228 @@ -68,6 +68,12 @@ int INPUT_joy_5200_min = 6; int INPUT_joy_5200_center = 114; int INPUT_joy_5200_max = 220; +#if defined(__LIBRETRO__) +int INPUT_digital_5200_min = 6; +int INPUT_digital_5200_center = 114; +int INPUT_digital_5200_max = 220; +#endif + int INPUT_cx85 = 0; int INPUT_mouse_mode = INPUT_MOUSE_OFF; @@ -625,15 +631,38 @@ void INPUT_Frame(void) } else { for (i = 0; i < 4; i++) { -#ifdef DREAMCAST +#if defined (DREAMCAST) /* first get analog js data */ POKEY_POT_input[2 * i] = Atari_POT(2 * i); /* x */ POKEY_POT_input[2 * i + 1] = Atari_POT(2 * i + 1); /* y */ if (POKEY_POT_input[2 * i] != INPUT_joy_5200_center || POKEY_POT_input[2 * i + 1] != INPUT_joy_5200_center) continue; - /* if analog js is unused, alternatively try keypad */ #endif +#if defined(__LIBRETRO__) + /* first get analog js data */ + POKEY_POT_input[2 * i] = Atari_POT(2 * i); /* x */ + POKEY_POT_input[2 * i + 1] = Atari_POT(2 * i + 1); /* y */ + if (POKEY_POT_input[2 * i] != INPUT_joy_5200_center + || POKEY_POT_input[2 * i + 1] != INPUT_joy_5200_center) + continue; + + /* if analog js is unused, alternatively try keypad */ + if ((STICK[i] & (INPUT_STICK_CENTRE ^ INPUT_STICK_LEFT)) == 0) + POKEY_POT_input[2 * i] = INPUT_digital_5200_min; + else if ((STICK[i] & (INPUT_STICK_CENTRE ^ INPUT_STICK_RIGHT)) == 0) + POKEY_POT_input[2 * i] = INPUT_digital_5200_max; + else + POKEY_POT_input[2 * i] = INPUT_digital_5200_center; + if ((STICK[i] & (INPUT_STICK_CENTRE ^ INPUT_STICK_FORWARD)) == 0) + POKEY_POT_input[2 * i + 1] = INPUT_digital_5200_min; + else if ((STICK[i] & (INPUT_STICK_CENTRE ^ INPUT_STICK_BACK)) == 0) + POKEY_POT_input[2 * i + 1] = INPUT_digital_5200_max; + else + POKEY_POT_input[2 * i + 1] = INPUT_digital_5200_center; + } +#else + /* if analog js is unused, alternatively try keypad */ if ((STICK[i] & (INPUT_STICK_CENTRE ^ INPUT_STICK_LEFT)) == 0) POKEY_POT_input[2 * i] = INPUT_joy_5200_min; else if ((STICK[i] & (INPUT_STICK_CENTRE ^ INPUT_STICK_RIGHT)) == 0) @@ -647,6 +676,7 @@ void INPUT_Frame(void) else POKEY_POT_input[2 * i + 1] = INPUT_joy_5200_center; } +#endif } /* handle mouse */ diff --git a/atari800/src/memory.c b/atari800/src/memory.c index e816176..53e6220 100644 --- a/atari800/src/memory.c +++ b/atari800/src/memory.c @@ -339,6 +339,351 @@ void MEMORY_InitialiseMachine(void) #ifndef BASIC +#if defined(__LIBRETRO__) +void Retro_MEMORY_StateSave(UBYTE SaveVerbose) +{ + int temp; + UBYTE byte; + + /* Axlon/Mosaic for 400/800 */ + if (Atari800_machine_type == Atari800_MACHINE_800) { + Retro_SaveINT(&MEMORY_axlon_num_banks, 1); + if (MEMORY_axlon_num_banks > 0) { + Retro_SaveINT(&axlon_curbank, 1); + Retro_SaveINT(&MEMORY_axlon_0f_mirror, 1); + Retro_SaveUBYTE(axlon_ram, MEMORY_axlon_num_banks * 0x4000); + } + Retro_SaveINT(&mosaic_current_num_banks, 1); + if (mosaic_current_num_banks > 0) { + Retro_SaveINT(&mosaic_curbank, 1); + Retro_SaveUBYTE(mosaic_ram, mosaic_current_num_banks * 0x1000); + } + } + + /* Save amount of base RAM in kilobytes. */ + temp = MEMORY_ram_size > 64 ? 64 : MEMORY_ram_size; + Retro_SaveINT(&temp, 1); + Retro_SaveUBYTE(&MEMORY_mem[0], 65536); +#ifndef PAGED_ATTRIB + Retro_SaveUBYTE(&MEMORY_attrib[0], 65536); +#else + { + /* I assume here that consecutive calls to StateSav_SaveUBYTE() + are equivalent to a single call with all the values + (i.e. StateSav_SaveUBYTE() doesn't write any headers). */ + UBYTE attrib_page[256]; + int i; + for (i = 0; i < 256; i++) { + if (MEMORY_writemap[i] == NULL) + memset(attrib_page, MEMORY_RAM, 256); + else if (MEMORY_writemap[i] == MEMORY_ROM_PutByte) + memset(attrib_page, MEMORY_ROM, 256); + else if (i == 0x4f || i == 0x5f || i == 0x8f || i == 0x9f) { + /* special case: Bounty Bob bank switching registers */ + memset(attrib_page, MEMORY_ROM, 256); + attrib_page[0xf6] = MEMORY_HARDWARE; + attrib_page[0xf7] = MEMORY_HARDWARE; + attrib_page[0xf8] = MEMORY_HARDWARE; + attrib_page[0xf9] = MEMORY_HARDWARE; + } + else { + memset(attrib_page, MEMORY_HARDWARE, 256); + } + Retro_SaveUBYTE(&attrib_page[0], 256); + } + } +#endif + + if (Atari800_machine_type == Atari800_MACHINE_XLXE) { + if (SaveVerbose != 0) + Retro_SaveUBYTE(&MEMORY_basic[0], 8192); + Retro_SaveUBYTE(&under_cartA0BF[0], 8192); + + if (SaveVerbose != 0) + Retro_SaveUBYTE(&MEMORY_os[0], 16384); + Retro_SaveUBYTE(&under_atarixl_os[0], 16384); + if (SaveVerbose != 0) + Retro_SaveUBYTE(MEMORY_xegame, 0x2000); + } + + /* Save amount of XE RAM in 16KB banks. */ + temp = (MEMORY_ram_size - 64) / 16; + if (temp < 0) + temp = 0; + Retro_SaveINT(&temp, 1); + if (MEMORY_ram_size == MEMORY_RAM_320_RAMBO || MEMORY_ram_size == MEMORY_RAM_320_COMPY_SHOP) { + /* Save specific banking type. */ + temp = MEMORY_ram_size - 320; + Retro_SaveINT(&temp, 1); + } + byte = PIA_PORTB | PIA_PORTB_mask; + Retro_SaveUBYTE(&byte, 1); + + Retro_SaveINT(&MEMORY_cartA0BF_enabled, 1); + + if (MEMORY_ram_size > 64) { + Retro_SaveUBYTE(&atarixe_memory[0], atarixe_memory_size); + if (ANTIC_xe_ptr != NULL && MEMORY_selftest_enabled) + Retro_SaveUBYTE(antic_bank_under_selftest, 0x800); + } + + /* Simius XL/XE MapRAM expansion */ + if (Atari800_machine_type == Atari800_MACHINE_XLXE && MEMORY_ram_size > 20) { + Retro_SaveINT(&MEMORY_enable_mapram, 1); + if (MEMORY_enable_mapram) { + Retro_SaveUBYTE(mapram_memory, 0x800); + } + } +} + +void Retro_MEMORY_StateRead(UBYTE SaveVerbose, UBYTE StateVersion) +{ + int base_ram_kb; + int num_xe_banks; + UBYTE portb; + + /* Axlon/Mosaic for 400/800 */ + if (Atari800_machine_type == Atari800_MACHINE_800 && StateVersion >= 5) { + Retro_ReadINT(&MEMORY_axlon_num_banks, 1); + if (MEMORY_axlon_num_banks > 0) { + Retro_ReadINT(&axlon_curbank, 1); + if (StateVersion < 7) { + /* Read bank mask, then increase by 1 to get number of banks. */ + Retro_ReadINT(&MEMORY_axlon_num_banks, 1); + ++MEMORY_axlon_num_banks; + } + Retro_ReadINT(&MEMORY_axlon_0f_mirror, 1); + if (StateVersion < 7) { + int temp; + /* Ignore saved RAM size - can be derived. */ + Retro_ReadINT(&temp, 1); + } + alloc_axlon_memory(); + Retro_ReadUBYTE(axlon_ram, MEMORY_axlon_num_banks * 0x4000); + } + Retro_ReadINT(&MEMORY_mosaic_num_banks, 1); + if (MEMORY_mosaic_num_banks > 0) { + Retro_ReadINT(&mosaic_curbank, 1); + if (StateVersion < 7) { + int temp; + /* Read max bank number, then increase by 1 to get number of banks. */ + Retro_ReadINT(&MEMORY_mosaic_num_banks, 1); + ++MEMORY_mosaic_num_banks; + Retro_ReadINT(&temp, 1); /* Ignore Mosaic RAM size - can be derived. */ + } + alloc_mosaic_memory(); + Retro_ReadUBYTE(mosaic_ram, mosaic_current_num_banks * 0x1000); + } + } + + if (StateVersion >= 7) + /* Read amount of base RAM in kilobytes. */ + Retro_ReadINT(&base_ram_kb, 1); + Retro_ReadUBYTE(&MEMORY_mem[0], 65536); +#ifndef PAGED_ATTRIB + Retro_ReadUBYTE(&MEMORY_attrib[0], 65536); +#else + { + UBYTE attrib_page[256]; + int i; + for (i = 0; i < 256; i++) { + Retro_ReadUBYTE(&attrib_page[0], 256); + /* note: 0x40 is intentional here: + we want ROM on page 0xd1 if H: patches are enabled */ + switch (attrib_page[0x40]) { + case MEMORY_RAM: + MEMORY_readmap[i] = NULL; + MEMORY_writemap[i] = NULL; + break; + case MEMORY_ROM: + if (i != 0xd1 && attrib_page[0xf6] == MEMORY_HARDWARE) { + if (i == 0x4f || i == 0x8f) { + MEMORY_readmap[i] = CARTRIDGE_BountyBob1GetByte; + MEMORY_writemap[i] = CARTRIDGE_BountyBob1PutByte; + } + else if (i == 0x5f || i == 0x9f) { + MEMORY_readmap[i] = CARTRIDGE_BountyBob2GetByte; + MEMORY_writemap[i] = CARTRIDGE_BountyBob2PutByte; + } + else if (i == 0xbf) { + MEMORY_readmap[i] = CARTRIDGE_5200SuperCartGetByte; + MEMORY_writemap[i] = CARTRIDGE_5200SuperCartPutByte; + } + /* else something's wrong, so we keep current values */ + } + else { + MEMORY_readmap[i] = NULL; + MEMORY_writemap[i] = MEMORY_ROM_PutByte; + } + break; + case MEMORY_HARDWARE: + switch (i) { + case 0xc0: + case 0xd0: + MEMORY_readmap[i] = GTIA_GetByte; + MEMORY_writemap[i] = GTIA_PutByte; + break; + case 0xd1: + MEMORY_readmap[i] = PBI_D1GetByte; + MEMORY_writemap[i] = PBI_D1PutByte; + break; + case 0xd2: + case 0xe8: + case 0xeb: + MEMORY_readmap[i] = POKEY_GetByte; + MEMORY_writemap[i] = POKEY_PutByte; + break; + case 0xd3: + MEMORY_readmap[i] = PIA_GetByte; + MEMORY_writemap[i] = PIA_PutByte; + break; + case 0xd4: + MEMORY_readmap[i] = ANTIC_GetByte; + MEMORY_writemap[i] = ANTIC_PutByte; + break; + case 0xd5: + MEMORY_readmap[i] = CARTRIDGE_GetByte; + MEMORY_writemap[i] = CARTRIDGE_PutByte; + break; + case 0xd6: + MEMORY_readmap[i] = PBI_D6GetByte; + MEMORY_writemap[i] = PBI_D6PutByte; + break; + case 0xd7: + MEMORY_readmap[i] = PBI_D7GetByte; + MEMORY_writemap[i] = PBI_D7PutByte; + break; + case 0xff: + if (MEMORY_mosaic_num_banks > 0) MEMORY_writemap[0xff] = MosaicPutByte; + break; + case 0xcf: + if (MEMORY_axlon_num_banks > 0) MEMORY_writemap[0xcf] = AxlonPutByte; + break; + case 0x0f: + if (MEMORY_axlon_num_banks > 0 && MEMORY_axlon_0f_mirror) MEMORY_writemap[0x0f] = AxlonPutByte; + break; + default: + /* something's wrong, so we keep current values */ + break; + } + break; + default: + /* something's wrong, so we keep current values */ + break; + } + } + } +#endif + + if (Atari800_machine_type == Atari800_MACHINE_XLXE) { + if (SaveVerbose) + Retro_ReadUBYTE(&MEMORY_basic[0], 8192); + Retro_ReadUBYTE(&under_cartA0BF[0], 8192); + + if (SaveVerbose) + Retro_ReadUBYTE(&MEMORY_os[0], 16384); + Retro_ReadUBYTE(&under_atarixl_os[0], 16384); + if (StateVersion >= 7 && SaveVerbose) + Retro_ReadUBYTE(MEMORY_xegame, 0x2000); + } + + if (StateVersion >= 7) { + /* Read amount of XE RAM in 16KB banks. */ + Retro_ReadINT(&num_xe_banks, 1); + /* Compute value of MEMORY_ram_size. */ + MEMORY_ram_size = base_ram_kb + num_xe_banks * 16; + if (MEMORY_ram_size == 320) { + /* There are 2 different memory mappings for 320 KB. */ + /* In savestate version <= 6 this variable is read in PIA_StateRead. */ + int xe_type; + Retro_ReadINT(&xe_type, 1); + MEMORY_ram_size += xe_type; + } + if (!MEMORY_SizeValid(MEMORY_ram_size)) { + MEMORY_ram_size = 64; + Log_print("Warning: Bad RAM size read in from state save, defaulting to 64 KB"); + } + + /* Read PORTB and set variables that are based on it. */ + Retro_ReadUBYTE(&portb, 1); + MEMORY_xe_bank = 0; + if (MEMORY_ram_size > 64 && (portb & 0x30) != 0x30) { + switch (MEMORY_ram_size) { + case 128: + MEMORY_xe_bank = ((portb & 0x0c) >> 2) + 1; + break; + case 192: + MEMORY_xe_bank = (((portb & 0x0c) + ((portb & 0x40) >> 2)) >> 2) + 1; + break; + case MEMORY_RAM_320_RAMBO: + MEMORY_xe_bank = (((portb & 0x0c) + ((portb & 0x60) >> 1)) >> 2) + 1; + break; + case MEMORY_RAM_320_COMPY_SHOP: + MEMORY_xe_bank = (((portb & 0x0c) + ((portb & 0xc0) >> 2)) >> 2) + 1; + break; + case 576: + MEMORY_xe_bank = (((portb & 0x0e) + ((portb & 0x60) >> 1)) >> 1) + 1; + break; + case 1088: + MEMORY_xe_bank = (((portb & 0x0e) + ((portb & 0xe0) >> 1)) >> 1) + 1; + break; + } + } + /* In savestate version <= 6 this variable is read in PIA_StateRead. */ + MEMORY_selftest_enabled = (portb & 0x81) == 0x01 + && !((portb & 0x30) != 0x30 && MEMORY_ram_size == MEMORY_RAM_320_COMPY_SHOP) + && !((portb & 0x10) == 0 && MEMORY_ram_size == 1088); + + Retro_ReadINT(&MEMORY_cartA0BF_enabled, 1); + if (Atari800_machine_type == Atari800_MACHINE_XLXE) { + GTIA_TRIG[3] = MEMORY_cartA0BF_enabled; + if (MEMORY_cartA0BF_enabled == 0 && (GTIA_GRACTL & 4)) + GTIA_TRIG_latch[3] = 0; + } + } + ANTIC_xe_ptr = NULL; + AllocXEMemory(); + if (MEMORY_ram_size > 64) { + Retro_ReadUBYTE(&atarixe_memory[0], atarixe_memory_size); + /* a hack that makes state files compatible with previous versions: + for 130 XE there's written 192 KB of unused data */ + if (MEMORY_ram_size == 128 && StateVersion <= 6) { + UBYTE buffer[256]; + int i; + for (i = 0; i < 192 * 4; i++) + Retro_ReadUBYTE(&buffer[0], 256); + } + if (StateVersion >= 7 && (MEMORY_ram_size == 128 || MEMORY_ram_size == MEMORY_RAM_320_COMPY_SHOP)) { + switch (portb & 0x30) { + case 0x20: /* ANTIC: base, CPU: extended */ + ANTIC_xe_ptr = atarixe_memory; + break; + case 0x10: /* ANTIC: extended, CPU: base */ + ANTIC_xe_ptr = atarixe_memory + (MEMORY_xe_bank << 14); + break; + default: /* ANTIC same as CPU */ + ANTIC_xe_ptr = NULL; + break; + } + + if (ANTIC_xe_ptr != NULL && MEMORY_selftest_enabled) + /* Also read ANTIC-visible memory shadowed by Self Test. */ + Retro_ReadUBYTE(antic_bank_under_selftest, 0x800); + + } + } + + /* Simius XL/XE MapRAM expansion */ + if (StateVersion >= 7 && Atari800_machine_type == Atari800_MACHINE_XLXE && MEMORY_ram_size > 20) { + Retro_ReadINT(&MEMORY_enable_mapram, 1); + AllocMapRAM(); + if (mapram_memory != NULL) { + Retro_ReadUBYTE(mapram_memory, 0x800); + } + } +} +#endif /* __LIBRETRO__ */ + void MEMORY_StateSave(UBYTE SaveVerbose) { int temp; @@ -504,6 +849,10 @@ void MEMORY_StateRead(UBYTE SaveVerbose, UBYTE StateVersion) MEMORY_readmap[i] = CARTRIDGE_BountyBob2GetByte; MEMORY_writemap[i] = CARTRIDGE_BountyBob2PutByte; } + else if (i == 0xbf) { + MEMORY_readmap[i] = CARTRIDGE_5200SuperCartGetByte; + MEMORY_writemap[i] = CARTRIDGE_5200SuperCartPutByte; + } /* else something's wrong, so we keep current values */ } else { @@ -1068,13 +1417,16 @@ UBYTE MEMORY_HwGetByte(UWORD addr, int no_side_effects) case 0x8f00: if (!no_side_effects) CARTRIDGE_BountyBob1(addr); - byte = 0; + byte = MEMORY_dGetByte(addr); break; case 0x5f00: case 0x9f00: if (!no_side_effects) CARTRIDGE_BountyBob2(addr); - byte = 0; + byte = MEMORY_dGetByte(addr); + break; + case 0xbf00: + byte = CARTRIDGE_5200SuperCartGetByte(addr, no_side_effects); break; case 0xd000: /* GTIA */ case 0xc000: /* GTIA - 5200 */ @@ -1153,6 +1505,9 @@ void MEMORY_HwPutByte(UWORD addr, UBYTE byte) case 0x9f00: CARTRIDGE_BountyBob2(addr); break; + case 0xbf00: + CARTRIDGE_5200SuperCartPutByte(addr, byte); + break; case 0xd000: /* GTIA */ case 0xc000: /* GTIA - 5200 */ case 0xc100: /* GTIA - 5200 */ diff --git a/atari800/src/memory.h b/atari800/src/memory.h index 7518121..16e9c4a 100644 --- a/atari800/src/memory.h +++ b/atari800/src/memory.h @@ -109,6 +109,12 @@ int MEMORY_SizeValid(int size); void MEMORY_InitialiseMachine(void); void MEMORY_StateSave(UBYTE SaveVerbose); void MEMORY_StateRead(UBYTE SaveVerbose, UBYTE StateVersion); + +#if defined(__LIBRETRO__) +void Retro_MEMORY_StateSave(UBYTE SaveVerbose); +void Retro_MEMORY_StateRead(UBYTE SaveVerbose, UBYTE StateVersion); +#endif + void MEMORY_CopyFromMem(UWORD from, UBYTE *to, int size); void MEMORY_CopyToMem(const UBYTE *from, UWORD to, int size); void MEMORY_HandlePORTB(UBYTE byte, UBYTE oldval); diff --git a/atari800/src/pbi.c b/atari800/src/pbi.c index 441a4d4..ee76ecc 100644 --- a/atari800/src/pbi.c +++ b/atari800/src/pbi.c @@ -320,6 +320,22 @@ void PBI_D7PutByte(UWORD addr, UBYTE byte) #ifndef BASIC +#if defined(__LIBRETRO__) +void Retro_PBI_StateSave(void) +{ + Retro_SaveUBYTE(&D1FF_LATCH, 1); + Retro_SaveINT(&PBI_D6D7ram, 1); + Retro_SaveINT(&PBI_IRQ, 1); +} + +void Retro_PBI_StateRead(void) +{ + Retro_ReadUBYTE(&D1FF_LATCH, 1); + Retro_ReadINT(&PBI_D6D7ram, 1); + Retro_ReadINT(&PBI_IRQ, 1); +} +#endif + void PBI_StateSave(void) { StateSav_SaveUBYTE(&D1FF_LATCH, 1); diff --git a/atari800/src/pbi.h b/atari800/src/pbi.h index d473d9e..ea06cd0 100644 --- a/atari800/src/pbi.h +++ b/atari800/src/pbi.h @@ -19,6 +19,12 @@ extern int PBI_IRQ; extern int PBI_D6D7ram; void PBI_StateSave(void); void PBI_StateRead(void); + +#if defined(__LIBRETRO__) +void Retro_PBI_StateSave(void); +void Retro_PBI_StateRead(void); +#endif + #define PBI_NOT_HANDLED -1 /* #define PBI_DEBUG */ #endif /* PBI_H_ */ diff --git a/atari800/src/pia.c b/atari800/src/pia.c index 9c8093b..939723b 100644 --- a/atari800/src/pia.c +++ b/atari800/src/pia.c @@ -332,6 +332,73 @@ void PIA_PutByte(UWORD addr, UBYTE byte) #ifndef BASIC +#if defined(__LIBRETRO__) +void Retro_PIA_StateSave(void) +{ + Retro_SaveUBYTE(&PIA_PACTL, 1); + Retro_SaveUBYTE(&PIA_PBCTL, 1); + Retro_SaveUBYTE(&PIA_PORTA, 1); + Retro_SaveUBYTE(&PIA_PORTB, 1); + + Retro_SaveUBYTE(&PIA_PORTA_mask, 1); + Retro_SaveUBYTE(&PIA_PORTB_mask, 1); + Retro_SaveINT(&PIA_CA2, 1); + Retro_SaveINT(&PIA_CA2_negpending, 1); + Retro_SaveINT(&PIA_CA2_pospending, 1); + Retro_SaveINT(&PIA_CB2, 1); + Retro_SaveINT(&PIA_CB2_negpending, 1); + Retro_SaveINT(&PIA_CB2_pospending, 1); +} + +void Retro_PIA_StateRead(UBYTE version) +{ + UBYTE byte; + int temp; + + Retro_ReadUBYTE(&byte, 1); + if (version <= 7) { + PIA_PutByte(PIA_OFFSET_PACTL, byte); /* set PIA_CA2 and tape motor */ + } + PIA_PACTL = byte; + Retro_ReadUBYTE(&byte, 1); + if (version <= 7) { + PIA_PutByte(PIA_OFFSET_PBCTL, byte); /* set PIA_CB2 and !command line */ + } + PIA_PBCTL = byte; + Retro_ReadUBYTE(&PIA_PORTA, 1); + Retro_ReadUBYTE(&PIA_PORTB, 1); + + /* In version 7 and later these variables are read in memory.c. */ + if (version <= 6) { + int Ram256; + Retro_ReadINT(&MEMORY_xe_bank, 1); + Retro_ReadINT(&MEMORY_selftest_enabled, 1); + Retro_ReadINT(&Ram256, 1); + + if (Atari800_machine_type == Atari800_MACHINE_XLXE) { + if (Ram256 == 1 && MEMORY_ram_size == MEMORY_RAM_320_COMPY_SHOP) + MEMORY_ram_size = MEMORY_RAM_320_RAMBO; + } + Retro_ReadINT(&MEMORY_cartA0BF_enabled, 1); + } + + Retro_ReadUBYTE(&PIA_PORTA_mask, 1); + Retro_ReadUBYTE(&PIA_PORTB_mask, 1); + + if (version >= 8) { + Retro_ReadINT(&temp, 1); + set_CA2(temp); + Retro_ReadINT(&PIA_CA2_negpending, 1); + Retro_ReadINT(&PIA_CA2_pospending, 1); + Retro_ReadINT(&temp, 1); + set_CB2(temp); + Retro_ReadINT(&PIA_CB2_negpending, 1); + Retro_ReadINT(&PIA_CB2_pospending, 1); + update_PIA_IRQ(); + } +} +#endif /* __LIBRETRO__ */ + void PIA_StateSave(void) { StateSav_SaveUBYTE( &PIA_PACTL, 1 ); diff --git a/atari800/src/pia.h b/atari800/src/pia.h index a86dd24..6524a5e 100644 --- a/atari800/src/pia.h +++ b/atari800/src/pia.h @@ -26,4 +26,9 @@ void PIA_PutByte(UWORD addr, UBYTE byte); void PIA_StateSave(void); void PIA_StateRead(UBYTE version); +#if defined(__LIBRETRO__) +void Retro_PIA_StateSave(void); +void Retro_PIA_StateRead(UBYTE version); +#endif + #endif /* PIA_H_ */ diff --git a/atari800/src/pokey.c b/atari800/src/pokey.c index b1f4440..bb9587d 100644 --- a/atari800/src/pokey.c +++ b/atari800/src/pokey.c @@ -634,6 +634,80 @@ static void Update_Counter(int chan_mask) #ifndef BASIC +#if defined(__LIBRETRO__) +void Retro_POKEY_StateSave(void) +{ + int shift_key = 0; + int keypressed = 0; + UWORD random_scanline_counter_lo; + UWORD random_scanline_counter_hi; + + Retro_SaveUBYTE(&POKEY_KBCODE, 1); + Retro_SaveUBYTE(&POKEY_IRQST, 1); + Retro_SaveUBYTE(&POKEY_IRQEN, 1); + Retro_SaveUBYTE(&POKEY_SKCTL, 1); + + Retro_SaveINT(&shift_key, 1); + Retro_SaveINT(&keypressed, 1); + Retro_SaveINT(&POKEY_DELAYED_SERIN_IRQ, 1); + Retro_SaveINT(&POKEY_DELAYED_SEROUT_IRQ, 1); + Retro_SaveINT(&POKEY_DELAYED_XMTDONE_IRQ, 1); + + Retro_SaveUBYTE(&POKEY_AUDF[0], 4); + Retro_SaveUBYTE(&POKEY_AUDC[0], 4); + Retro_SaveUBYTE(&POKEY_AUDCTL[0], 1); + + Retro_SaveINT(&POKEY_DivNIRQ[0], 4); + Retro_SaveINT(&POKEY_DivNMax[0], 4); + Retro_SaveINT(&POKEY_Base_mult[0], 1); + + // Store the random number generator for runahead and netplay. It's not seed based on the Atari like it is on other systems + random_scanline_counter_lo = random_scanline_counter & 0xFFFF; + random_scanline_counter_hi = (random_scanline_counter >> 16) & 0xFFFF; + + Retro_SaveUWORD(&random_scanline_counter_lo, 1); + Retro_SaveUWORD(&random_scanline_counter_hi, 1); +} + +void Retro_POKEY_StateRead(void) +{ + int i, shift_key, keypressed; + UWORD random_scanline_counter_lo = 0; + UWORD random_scanline_counter_hi = 0; + + Retro_ReadUBYTE(&POKEY_KBCODE, 1); + Retro_ReadUBYTE(&POKEY_IRQST, 1); + Retro_ReadUBYTE(&POKEY_IRQEN, 1); + Retro_ReadUBYTE(&POKEY_SKCTL, 1); + + Retro_ReadINT(&shift_key, 1); + Retro_ReadINT(&keypressed, 1); + Retro_ReadINT(&POKEY_DELAYED_SERIN_IRQ, 1); + Retro_ReadINT(&POKEY_DELAYED_SEROUT_IRQ, 1); + Retro_ReadINT(&POKEY_DELAYED_XMTDONE_IRQ, 1); + + Retro_ReadUBYTE(&POKEY_AUDF[0], 4); + Retro_ReadUBYTE(&POKEY_AUDC[0], 4); + Retro_ReadUBYTE(&POKEY_AUDCTL[0], 1); + for (i = 0; i < 4; i++) { + POKEY_PutByte((UWORD)(POKEY_OFFSET_AUDF1 + i * 2), POKEY_AUDF[i]); + POKEY_PutByte((UWORD)(POKEY_OFFSET_AUDC1 + i * 2), POKEY_AUDC[i]); + } + POKEY_PutByte(POKEY_OFFSET_AUDCTL, POKEY_AUDCTL[0]); + + Retro_ReadINT(&POKEY_DivNIRQ[0], 4); + Retro_ReadINT(&POKEY_DivNMax[0], 4); + Retro_ReadINT(&POKEY_Base_mult[0], 1); + + Retro_ReadUWORD(&random_scanline_counter_lo, 1); + Retro_ReadUWORD(&random_scanline_counter_hi, 1); + + // Restore the random number generator for runahead and netplay. It's not seed based on the Atari like it is on other systems + random_scanline_counter = (random_scanline_counter_hi << 16) | + random_scanline_counter_lo; +} +#endif /* __LIBRETRO__ */ + void POKEY_StateSave(void) { int shift_key = 0; diff --git a/atari800/src/pokey.h b/atari800/src/pokey.h index b4fa847..7581378 100644 --- a/atari800/src/pokey.h +++ b/atari800/src/pokey.h @@ -63,6 +63,11 @@ void POKEY_Scanline(void); void POKEY_StateSave(void); void POKEY_StateRead(void); +#if defined(__LIBRETRO__) +void Retro_POKEY_StateSave(void); +void Retro_POKEY_StateRead(void); +#endif + #endif /* CONSTANT DEFINITIONS */ diff --git a/atari800/src/pokeysnd.c b/atari800/src/pokeysnd.c index b32375b..a77c37e 100644 --- a/atari800/src/pokeysnd.c +++ b/atari800/src/pokeysnd.c @@ -1,1393 +1,1397 @@ -/* - * pokeysnd.c - POKEY sound chip emulation, v2.4 - * - * Copyright (C) 1996-1998 Ron Fries - * Copyright (C) 1998-2014 Atari800 development team (see DOC/CREDITS) - * - * This file is part of the Atari800 emulator project which emulates - * the Atari 400, 800, 800XL, 130XE, and 5200 8-bit computers. - * - * Atari800 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 2 of the License, or - * (at your option) any later version. - * - * Atari800 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 Atari800; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA -*/ - -#include "config.h" -#include -#include - -#ifdef ASAP /* external project, see http://asap.sf.net */ -#include "asap_internal.h" -#else -#include "atari.h" -#ifndef __PLUS -#include "sndsave.h" -#else -#include "sound_win.h" -#endif -#endif -#include "mzpokeysnd.h" -#include "pokeysnd.h" -#if defined(PBI_XLD) || defined (VOICEBOX) -#include "votraxsnd.h" -#endif -#include "antic.h" -#include "gtia.h" -#include "util.h" - -#ifdef WORDS_UNALIGNED_OK -# define READ_U32(x) (*(ULONG *) (x)) -# define WRITE_U32(x, d) (*(ULONG *) (x) = (d)) -#else -# ifdef WORDS_BIGENDIAN -# define READ_U32(x) (((*(unsigned char *)(x)) << 24) | ((*((unsigned char *)(x) + 1)) << 16) | \ - ((*((unsigned char *)(x) + 2)) << 8) | ((*((unsigned char *)(x) + 3)))) -# define WRITE_U32(x, d) \ - { \ - ULONG i = d; \ - (*(unsigned char *) (x)) = (((i) >> 24) & 255); \ - (*((unsigned char *) (x) + 1)) = (((i) >> 16) & 255); \ - (*((unsigned char *) (x) + 2)) = (((i) >> 8) & 255); \ - (*((unsigned char *) (x) + 3)) = ((i) & 255); \ - } -# else -# define READ_U32(x) ((*(unsigned char *) (x)) | ((*((unsigned char *) (x) + 1)) << 8) | \ - ((*((unsigned char *) (x) + 2)) << 16) | ((*((unsigned char *) (x) + 3)) << 24)) -# define WRITE_U32(x, d) \ - { \ - ULONG i = d; \ - (*(unsigned char *)(x)) = ((i) & 255); \ - (*((unsigned char *)(x) + 1)) = (((i) >> 8) & 255); \ - (*((unsigned char *)(x) + 2)) = (((i) >> 16) & 255); \ - (*((unsigned char *)(x) + 3)) = (((i) >> 24) & 255); \ - } -# endif -#endif - -/* GLOBAL VARIABLE DEFINITIONS */ - -/* number of pokey chips currently emulated */ -static UBYTE Num_pokeys; - -static UBYTE pokeysnd_AUDV[4 * POKEY_MAXPOKEYS]; /* Channel volume - derived */ - -static UBYTE Outbit[4 * POKEY_MAXPOKEYS]; /* current state of the output (high or low) */ - -static UBYTE Outvol[4 * POKEY_MAXPOKEYS]; /* last output volume for each channel */ - -/* Initialize the bit patterns for the polynomials. */ - -/* The 4bit and 5bit patterns are the identical ones used in the pokey chip. */ -/* Though the patterns could be packed with 8 bits per byte, using only a */ -/* single bit per byte keeps the math simple, which is important for */ -/* efficient processing. */ - -static UBYTE bit4[POKEY_POLY4_SIZE] = -#ifndef POKEY23_POLY -{1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 1, 1, 0, 1, 0}; /* new table invented by Perry */ -#else -{1, 1, 0, 1, 1, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0}; /* original POKEY 2.3 table */ -#endif - -static UBYTE bit5[POKEY_POLY5_SIZE] = -#ifndef POKEY23_POLY -{1, 1, 1, 1, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0}; -#else -{0, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 0, 0, 1, 0, 1, 0, 1, 1, 0, 1, 1, 1, 0, 1, 0, 0, 0, 0, 0, 1}; -#endif - -static ULONG P4 = 0, /* Global position pointer for the 4-bit POLY array */ - P5 = 0, /* Global position pointer for the 5-bit POLY array */ - P9 = 0, /* Global position pointer for the 9-bit POLY array */ - P17 = 0; /* Global position pointer for the 17-bit POLY array */ - -static ULONG Div_n_cnt[4 * POKEY_MAXPOKEYS], /* Divide by n counter. one for each channel */ - Div_n_max[4 * POKEY_MAXPOKEYS]; /* Divide by n maximum, one for each channel */ - -static ULONG Samp_n_max, /* Sample max. For accuracy, it is *256 */ - Samp_n_cnt[2]; /* Sample cnt. */ - -#ifdef INTERPOLATE_SOUND -static UWORD last_val = 0; /* last output value */ -#ifdef STEREO_SOUND -static UWORD last_val2 = 0; /* last output value */ -#endif -#endif - -/* Volume only emulations declarations */ -#ifdef VOL_ONLY_SOUND - -int POKEYSND_sampbuf_val[POKEYSND_SAMPBUF_MAX]; /* volume values */ -int POKEYSND_sampbuf_cnt[POKEYSND_SAMPBUF_MAX]; /* relative start time */ -int POKEYSND_sampbuf_ptr = 0; /* pointer to sampbuf */ -int POKEYSND_sampbuf_rptr = 0; /* pointer to read from sampbuf */ -int POKEYSND_sampbuf_last = 0; /* last absolute time */ -int POKEYSND_sampbuf_AUDV[4 * POKEY_MAXPOKEYS]; /* prev. channel volume */ -int POKEYSND_sampbuf_lastval = 0; /* last volume */ -int POKEYSND_sampout; /* last out volume */ -int POKEYSND_samp_freq; -int POKEYSND_samp_consol_val = 0; /* actual value of console sound */ -#ifdef STEREO_SOUND -static int sampbuf_val2[POKEYSND_SAMPBUF_MAX]; /* volume values */ -static int sampbuf_cnt2[POKEYSND_SAMPBUF_MAX]; /* relative start time */ -static int sampbuf_ptr2 = 0; /* pointer to sampbuf */ -static int sampbuf_rptr2 = 0; /* pointer to read from sampbuf */ -static int sampbuf_last2 = 0; /* last absolute time */ -static int sampbuf_lastval2 = 0; /* last volume */ -static int sampout2; /* last out volume */ -#endif -#endif /* VOL_ONLY_SOUND */ - -static ULONG snd_freq17 = POKEYSND_FREQ_17_EXACT; -int POKEYSND_playback_freq = 44100; -UBYTE POKEYSND_num_pokeys = 1; -int POKEYSND_snd_flags = 0; -static int mz_quality = 0; /* default quality for mzpokeysnd */ -#ifdef __PLUS -int mz_clear_regs = 0; -#endif - -int POKEYSND_enable_new_pokey = TRUE; -int POKEYSND_bienias_fix = TRUE; /* when TRUE, high frequencies get emulated: better sound but slower */ -#if defined(__PLUS) && !defined(_WX_) -#define BIENIAS_FIX (g_Sound.nBieniasFix) -#else -#define BIENIAS_FIX POKEYSND_bienias_fix -#endif -#ifndef ASAP -int POKEYSND_stereo_enabled = FALSE; -#endif - -int POKEYSND_volume = 0x100; - -/* multiple sound engine interface */ -static void pokeysnd_process_8(void *sndbuffer, int sndn); -static void pokeysnd_process_16(void *sndbuffer, int sndn); -static void null_pokey_process(void *sndbuffer, int sndn) {} -void (*POKEYSND_Process_ptr)(void *sndbuffer, int sndn) = null_pokey_process; - -static void Update_pokey_sound_rf(UWORD, UBYTE, UBYTE, UBYTE); -static void null_pokey_sound(UWORD addr, UBYTE val, UBYTE chip, UBYTE gain) {} -void (*POKEYSND_Update_ptr) (UWORD addr, UBYTE val, UBYTE chip, UBYTE gain) - = null_pokey_sound; - -#ifdef SERIO_SOUND -static void Update_serio_sound_rf(int out, UBYTE data); -static void null_serio_sound(int out, UBYTE data) {} -void (*POKEYSND_UpdateSerio)(int out, UBYTE data) = null_serio_sound; -int POKEYSND_serio_sound_enabled = 1; -#endif - -#ifdef CONSOLE_SOUND -static void Update_consol_sound_rf(int set); -static void null_consol_sound(int set) {} -void (*POKEYSND_UpdateConsol_ptr)(int set) = null_consol_sound; -int POKEYSND_console_sound_enabled = 1; -#endif - -#ifdef VOL_ONLY_SOUND -static void Update_vol_only_sound_rf(void); -static void null_vol_only_sound(void) {} -void (*POKEYSND_UpdateVolOnly)(void) = null_vol_only_sound; -#endif - -#ifdef SYNCHRONIZED_SOUND -UBYTE *POKEYSND_process_buffer = NULL; -unsigned int POKEYSND_process_buffer_length; -unsigned int POKEYSND_process_buffer_fill; -static unsigned int prev_update_tick; - -static void Generate_sync_rf(unsigned int num_ticks); -static void null_generate_sync(unsigned int num_ticks) {} -void (*POKEYSND_GenerateSync)(unsigned int num_ticks) = null_generate_sync; - -static double ticks_per_sample; -static double samp_pos; -static int speaker; -static int const CONSOLE_VOL = 32; -#endif /* SYNCHRONIZED_SOUND */ - -/*****************************************************************************/ -/* In my routines, I treat the sample output as another divide by N counter */ -/* For better accuracy, the Samp_n_cnt has a fixed binary decimal point */ -/* which has 8 binary digits to the right of the decimal point. I use a two */ -/* byte array to give me a minimum of 40 bits, and then use pointer math to */ -/* reference either the 24.8 whole/fraction combination or the 32-bit whole */ -/* only number. This is mainly used to keep the math simple for */ -/* optimization. See below: */ -/* */ -/* Representation on little-endian machines: */ -/* xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx | xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx */ -/* fraction whole whole whole whole unused unused unused */ -/* */ -/* Samp_n_cnt[0] gives me a 32-bit int 24 whole bits with 8 fractional bits, */ -/* while (ULONG *)((UBYTE *)(&Samp_n_cnt[0])+1) gives me the 32-bit whole */ -/* number only. */ -/* */ -/* Representation on big-endian machines: */ -/* xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx | xxxxxxxx xxxxxxxx xxxxxxxx.xxxxxxxx */ -/* unused unused unused whole whole whole whole fraction */ -/* */ -/* Samp_n_cnt[1] gives me a 32-bit int 24 whole bits with 8 fractional bits, */ -/* while (ULONG *)((UBYTE *)(&Samp_n_cnt[0])+3) gives me the 32-bit whole */ -/* number only. */ -/*****************************************************************************/ - - -/*****************************************************************************/ -/* Module: pokeysnd_init_rf() */ -/* Purpose: to handle the power-up initialization functions */ -/* these functions should only be executed on a cold-restart */ -/* */ -/* Author: Ron Fries */ -/* Date: January 1, 1997 */ -/* */ -/* Inputs: freq17 - the value for the '1.79MHz' Pokey audio clock */ -/* playback_freq - the playback frequency in samples per second */ -/* num_pokeys - specifies the number of pokey chips to be emulated */ -/* */ -/* Outputs: Adjusts local globals - no return value */ -/* */ -/*****************************************************************************/ - -static int pokeysnd_init_rf(ULONG freq17, int playback_freq, - UBYTE num_pokeys, int flags); - -#ifdef VOL_ONLY_SOUND -/* Initialise variables related to volume-only sound. */ -static void init_vol_only(void) -{ - POKEYSND_sampbuf_rptr = POKEYSND_sampbuf_ptr; - POKEYSND_sampbuf_last = ANTIC_CPU_CLOCK; - POKEYSND_sampbuf_lastval = 0; - POKEYSND_samp_consol_val = 0; -#ifdef STEREO_SOUND - sampbuf_rptr2 = sampbuf_ptr2; - sampbuf_last2 = ANTIC_CPU_CLOCK; - sampbuf_lastval2 = 0; -#endif /* STEREO_SOUND */ -} -#endif /* VOL_ONLY_SOUND */ - -int POKEYSND_DoInit(void) -{ - SndSave_CloseSoundFile(); - -#ifdef VOL_ONLY_SOUND - init_vol_only(); -#endif /* VOL_ONLY_SOUND */ - - if (POKEYSND_enable_new_pokey) - return MZPOKEYSND_Init(snd_freq17, POKEYSND_playback_freq, - POKEYSND_num_pokeys, POKEYSND_snd_flags, mz_quality -#ifdef __PLUS - , mz_clear_regs -#endif - ); - else - return pokeysnd_init_rf(snd_freq17, POKEYSND_playback_freq, - POKEYSND_num_pokeys, POKEYSND_snd_flags); -} - -int POKEYSND_Init(ULONG freq17, int playback_freq, UBYTE num_pokeys, - int flags -#ifdef __PLUS - , int clear_regs -#endif -) -{ - snd_freq17 = freq17; - POKEYSND_playback_freq = playback_freq; - POKEYSND_num_pokeys = num_pokeys; - POKEYSND_snd_flags = flags; -#ifdef __PLUS - mz_clear_regs = clear_regs; -#endif -#ifdef SYNCHRONIZED_SOUND - { - /* A single call to Atari800_Frame may emulate a bit more CPU ticks than the exact number of - ticks per frame (Atari800_tv_mode*114). So we add a few ticks to buffer size just to be safe. */ - unsigned int const surplus_ticks = 10; - double samples_per_frame = (double)POKEYSND_playback_freq/(Atari800_tv_mode == Atari800_TV_PAL ? Atari800_FPS_PAL : Atari800_FPS_NTSC); - unsigned int ticks_per_frame = Atari800_tv_mode*114; - unsigned int max_ticks_per_frame = ticks_per_frame + surplus_ticks; - double ticks_per_sample = (double)ticks_per_frame / samples_per_frame; - POKEYSND_process_buffer_length = POKEYSND_num_pokeys * (unsigned int)ceil((double)max_ticks_per_frame / ticks_per_sample) * ((POKEYSND_snd_flags & POKEYSND_BIT16) ? 2:1); - free(POKEYSND_process_buffer); - POKEYSND_process_buffer = (UBYTE *)Util_malloc(POKEYSND_process_buffer_length); - POKEYSND_process_buffer_fill = 0; - prev_update_tick = ANTIC_CPU_CLOCK; - } -#endif /* SYNCHRONIZED_SOUND */ - -#if defined(PBI_XLD) || defined (VOICEBOX) - VOTRAXSND_Init(playback_freq, num_pokeys, (flags & POKEYSND_BIT16)); -#endif - return POKEYSND_DoInit(); -} - -void POKEYSND_SetMzQuality(int quality) /* specially for win32, perhaps not needed? */ -{ - mz_quality = quality; -} - -void POKEYSND_Process(void *sndbuffer, int sndn) -{ - POKEYSND_Process_ptr(sndbuffer, sndn); -#if defined(PBI_XLD) || defined (VOICEBOX) - VOTRAXSND_Process(sndbuffer,sndn); -#endif -#if !defined(__PLUS) && !defined(ASAP) - SndSave_WriteToSoundFile((const unsigned char *)sndbuffer, sndn); -#endif -} - -#ifdef SYNCHRONIZED_SOUND -static void Update_synchronized_sound(void) -{ - POKEYSND_GenerateSync(ANTIC_CPU_CLOCK - prev_update_tick); - prev_update_tick = ANTIC_CPU_CLOCK; -} - -int POKEYSND_UpdateProcessBuffer(void) -{ - int sndn; - Update_synchronized_sound(); - sndn = POKEYSND_process_buffer_fill / ((POKEYSND_snd_flags & POKEYSND_BIT16) ? 2 : 1); - POKEYSND_process_buffer_fill = 0; - -#if defined(PBI_XLD) || defined (VOICEBOX) - VOTRAXSND_Process(POKEYSND_process_buffer, sndn); -#endif -#if !defined(__PLUS) && !defined(ASAP) - SndSave_WriteToSoundFile((const unsigned char *)POKEYSND_process_buffer, sndn); -#endif - return sndn; -} -#endif /* SYNCHRONIZED_SOUND */ - -#ifdef SYNCHRONIZED_SOUND -static void init_syncsound(void) -{ - double samples_per_frame = (double)POKEYSND_playback_freq/(Atari800_tv_mode == Atari800_TV_PAL ? Atari800_FPS_PAL : Atari800_FPS_NTSC); - unsigned int ticks_per_frame = Atari800_tv_mode*114; - ticks_per_sample = (double)ticks_per_frame / samples_per_frame; - samp_pos = 0.0; - POKEYSND_GenerateSync = Generate_sync_rf; - speaker = 0; -} -#endif /* SYNCHRONIZED_SOUND */ - -static int pokeysnd_init_rf(ULONG freq17, int playback_freq, - UBYTE num_pokeys, int flags) -{ - UBYTE chan; - - POKEYSND_Update_ptr = Update_pokey_sound_rf; -#ifdef SERIO_SOUND - POKEYSND_UpdateSerio = Update_serio_sound_rf; -#endif -#ifdef CONSOLE_SOUND - POKEYSND_UpdateConsol_ptr = Update_consol_sound_rf; -#endif -#ifdef VOL_ONLY_SOUND - POKEYSND_UpdateVolOnly = Update_vol_only_sound_rf; -#endif - - POKEYSND_Process_ptr = (flags & POKEYSND_BIT16) ? pokeysnd_process_16 : pokeysnd_process_8; - -#ifdef VOL_ONLY_SOUND - POKEYSND_samp_freq = playback_freq; -#endif - - /* start all of the polynomial counters at zero */ - P4 = 0; - P5 = 0; - P9 = 0; - P17 = 0; - - /* calculate the sample 'divide by N' value based on the playback freq. */ - Samp_n_max = ((ULONG) freq17 << 8) / playback_freq; - - Samp_n_cnt[0] = 0; /* initialize all bits of the sample */ - Samp_n_cnt[1] = 0; /* 'divide by N' counter */ - - for (chan = 0; chan < (POKEY_MAXPOKEYS * 4); chan++) { - Outvol[chan] = 0; - Outbit[chan] = 0; - Div_n_cnt[chan] = 0; - Div_n_max[chan] = 0x7fffffffL; - pokeysnd_AUDV[chan] = 0; -#ifdef VOL_ONLY_SOUND - POKEYSND_sampbuf_AUDV[chan] = 0; -#endif - } - - /* set the number of pokey chips currently emulated */ - Num_pokeys = num_pokeys; - -#ifdef SYNCHRONIZED_SOUND - init_syncsound(); -#endif - return 0; /* OK */ -} - - -/*****************************************************************************/ -/* Module: Update_pokey_sound_rf() */ -/* Purpose: To process the latest control values stored in the AUDF, AUDC, */ -/* and AUDCTL registers. It pre-calculates as much information as */ -/* possible for better performance. This routine has not been */ -/* optimized. */ -/* */ -/* Author: Ron Fries */ -/* Date: January 1, 1997 */ -/* */ -/* Inputs: addr - the address of the parameter to be changed */ -/* val - the new value to be placed in the specified address */ -/* gain - specified as an 8-bit fixed point number - use 1 for no */ -/* amplification (output is multiplied by gain) */ -/* */ -/* Outputs: Adjusts local globals - no return value */ -/* */ -/*****************************************************************************/ - -void POKEYSND_Update(UWORD addr, UBYTE val, UBYTE chip, UBYTE gain) -{ -#ifdef SYNCHRONIZED_SOUND - Update_synchronized_sound(); -#endif /* SYNCHRONIZED_SOUND */ - POKEYSND_Update_ptr(addr, val, chip, gain); -} - -static void Update_pokey_sound_rf(UWORD addr, UBYTE val, UBYTE chip, - UBYTE gain) -{ - ULONG new_val = 0; - UBYTE chan; - UBYTE chan_mask; - UBYTE chip_offs; - - /* calculate the chip_offs for the channel arrays */ - chip_offs = chip << 2; - - /* determine which address was changed */ - switch (addr & 0x0f) { - case POKEY_OFFSET_AUDF1: - /* POKEY_AUDF[POKEY_CHAN1 + chip_offs] = val; */ - chan_mask = 1 << POKEY_CHAN1; - if (POKEY_AUDCTL[chip] & POKEY_CH1_CH2) /* if ch 1&2 tied together */ - chan_mask |= 1 << POKEY_CHAN2; /* then also change on ch2 */ - break; - case POKEY_OFFSET_AUDC1: - /* POKEY_AUDC[POKEY_CHAN1 + chip_offs] = val; */ - pokeysnd_AUDV[POKEY_CHAN1 + chip_offs] = (val & POKEY_VOLUME_MASK) * gain; - chan_mask = 1 << POKEY_CHAN1; - break; - case POKEY_OFFSET_AUDF2: - /* POKEY_AUDF[POKEY_CHAN2 + chip_offs] = val; */ - chan_mask = 1 << POKEY_CHAN2; - break; - case POKEY_OFFSET_AUDC2: - /* POKEY_AUDC[POKEY_CHAN2 + chip_offs] = val; */ - pokeysnd_AUDV[POKEY_CHAN2 + chip_offs] = (val & POKEY_VOLUME_MASK) * gain; - chan_mask = 1 << POKEY_CHAN2; - break; - case POKEY_OFFSET_AUDF3: - /* POKEY_AUDF[POKEY_CHAN3 + chip_offs] = val; */ - chan_mask = 1 << POKEY_CHAN3; - if (POKEY_AUDCTL[chip] & POKEY_CH3_CH4) /* if ch 3&4 tied together */ - chan_mask |= 1 << POKEY_CHAN4; /* then also change on ch4 */ - break; - case POKEY_OFFSET_AUDC3: - /* POKEY_AUDC[POKEY_CHAN3 + chip_offs] = val; */ - pokeysnd_AUDV[POKEY_CHAN3 + chip_offs] = (val & POKEY_VOLUME_MASK) * gain; - chan_mask = 1 << POKEY_CHAN3; - break; - case POKEY_OFFSET_AUDF4: - /* POKEY_AUDF[POKEY_CHAN4 + chip_offs] = val; */ - chan_mask = 1 << POKEY_CHAN4; - break; - case POKEY_OFFSET_AUDC4: - /* POKEY_AUDC[POKEY_CHAN4 + chip_offs] = val; */ - pokeysnd_AUDV[POKEY_CHAN4 + chip_offs] = (val & POKEY_VOLUME_MASK) * gain; - chan_mask = 1 << POKEY_CHAN4; - break; - case POKEY_OFFSET_AUDCTL: - /* POKEY_AUDCTL[chip] = val; */ - chan_mask = 15; /* all channels */ - break; - default: - chan_mask = 0; - break; - } - - /************************************************************/ - /* As defined in the manual, the exact Div_n_cnt values are */ - /* different depending on the frequency and resolution: */ - /* 64 kHz or 15 kHz - AUDF + 1 */ - /* 1 MHz, 8-bit - AUDF + 4 */ - /* 1 MHz, 16-bit - POKEY_AUDF[POKEY_CHAN1]+256*POKEY_AUDF[POKEY_CHAN2] + 7 */ - /************************************************************/ - - /* only reset the channels that have changed */ - - if (chan_mask & (1 << POKEY_CHAN1)) { - /* process channel 1 frequency */ - if (POKEY_AUDCTL[chip] & POKEY_CH1_179) - new_val = POKEY_AUDF[POKEY_CHAN1 + chip_offs] + 4; - else - new_val = (POKEY_AUDF[POKEY_CHAN1 + chip_offs] + 1) * POKEY_Base_mult[chip]; - - if (new_val != Div_n_max[POKEY_CHAN1 + chip_offs]) { - Div_n_max[POKEY_CHAN1 + chip_offs] = new_val; - - if (Div_n_cnt[POKEY_CHAN1 + chip_offs] > new_val) { - Div_n_cnt[POKEY_CHAN1 + chip_offs] = new_val; - } - } - } - - if (chan_mask & (1 << POKEY_CHAN2)) { - /* process channel 2 frequency */ - if (POKEY_AUDCTL[chip] & POKEY_CH1_CH2) { - if (POKEY_AUDCTL[chip] & POKEY_CH1_179) - new_val = POKEY_AUDF[POKEY_CHAN2 + chip_offs] * 256 + - POKEY_AUDF[POKEY_CHAN1 + chip_offs] + 7; - else - new_val = (POKEY_AUDF[POKEY_CHAN2 + chip_offs] * 256 + - POKEY_AUDF[POKEY_CHAN1 + chip_offs] + 1) * POKEY_Base_mult[chip]; - } - else - new_val = (POKEY_AUDF[POKEY_CHAN2 + chip_offs] + 1) * POKEY_Base_mult[chip]; - - if (new_val != Div_n_max[POKEY_CHAN2 + chip_offs]) { - Div_n_max[POKEY_CHAN2 + chip_offs] = new_val; - - if (Div_n_cnt[POKEY_CHAN2 + chip_offs] > new_val) { - Div_n_cnt[POKEY_CHAN2 + chip_offs] = new_val; - } - } - } - - if (chan_mask & (1 << POKEY_CHAN3)) { - /* process channel 3 frequency */ - if (POKEY_AUDCTL[chip] & POKEY_CH3_179) - new_val = POKEY_AUDF[POKEY_CHAN3 + chip_offs] + 4; - else - new_val = (POKEY_AUDF[POKEY_CHAN3 + chip_offs] + 1) * POKEY_Base_mult[chip]; - - if (new_val != Div_n_max[POKEY_CHAN3 + chip_offs]) { - Div_n_max[POKEY_CHAN3 + chip_offs] = new_val; - - if (Div_n_cnt[POKEY_CHAN3 + chip_offs] > new_val) { - Div_n_cnt[POKEY_CHAN3 + chip_offs] = new_val; - } - } - } - - if (chan_mask & (1 << POKEY_CHAN4)) { - /* process channel 4 frequency */ - if (POKEY_AUDCTL[chip] & POKEY_CH3_CH4) { - if (POKEY_AUDCTL[chip] & POKEY_CH3_179) - new_val = POKEY_AUDF[POKEY_CHAN4 + chip_offs] * 256 + - POKEY_AUDF[POKEY_CHAN3 + chip_offs] + 7; - else - new_val = (POKEY_AUDF[POKEY_CHAN4 + chip_offs] * 256 + - POKEY_AUDF[POKEY_CHAN3 + chip_offs] + 1) * POKEY_Base_mult[chip]; - } - else - new_val = (POKEY_AUDF[POKEY_CHAN4 + chip_offs] + 1) * POKEY_Base_mult[chip]; - - if (new_val != Div_n_max[POKEY_CHAN4 + chip_offs]) { - Div_n_max[POKEY_CHAN4 + chip_offs] = new_val; - - if (Div_n_cnt[POKEY_CHAN4 + chip_offs] > new_val) { - Div_n_cnt[POKEY_CHAN4 + chip_offs] = new_val; - } - } - } - - /* if channel is volume only, set current output */ - for (chan = POKEY_CHAN1; chan <= POKEY_CHAN4; chan++) { - if (chan_mask & (1 << chan)) { - -#ifdef VOL_ONLY_SOUND - -#ifdef __PLUS - if (g_Sound.nDigitized) -#endif - if ((POKEY_AUDC[chan + chip_offs] & POKEY_VOL_ONLY)) { - -#ifdef STEREO_SOUND - -#ifdef __PLUS - if (POKEYSND_stereo_enabled && chip & 0x01) -#else - if (chip & 0x01) -#endif - { - sampbuf_lastval2 += pokeysnd_AUDV[chan + chip_offs] - - POKEYSND_sampbuf_AUDV[chan + chip_offs]; - - sampbuf_val2[sampbuf_ptr2] = sampbuf_lastval2; - POKEYSND_sampbuf_AUDV[chan + chip_offs] = pokeysnd_AUDV[chan + chip_offs]; - sampbuf_cnt2[sampbuf_ptr2] = - (ANTIC_CPU_CLOCK - sampbuf_last2) * 128 * POKEYSND_samp_freq / 178979; - sampbuf_last2 = ANTIC_CPU_CLOCK; - sampbuf_ptr2++; - if (sampbuf_ptr2 >= POKEYSND_SAMPBUF_MAX) - sampbuf_ptr2 = 0; - if (sampbuf_ptr2 == sampbuf_rptr2) { - sampbuf_rptr2++; - if (sampbuf_rptr2 >= POKEYSND_SAMPBUF_MAX) - sampbuf_rptr2 = 0; - } - } - else -#endif /* STEREO_SOUND */ - { - POKEYSND_sampbuf_lastval += pokeysnd_AUDV[chan + chip_offs] - -POKEYSND_sampbuf_AUDV[chan + chip_offs]; - - POKEYSND_sampbuf_val[POKEYSND_sampbuf_ptr] = POKEYSND_sampbuf_lastval; - POKEYSND_sampbuf_AUDV[chan + chip_offs] = pokeysnd_AUDV[chan + chip_offs]; - POKEYSND_sampbuf_cnt[POKEYSND_sampbuf_ptr] = - (ANTIC_CPU_CLOCK - POKEYSND_sampbuf_last) * 128 * POKEYSND_samp_freq / 178979; - POKEYSND_sampbuf_last = ANTIC_CPU_CLOCK; - POKEYSND_sampbuf_ptr++; - if (POKEYSND_sampbuf_ptr >= POKEYSND_SAMPBUF_MAX) - POKEYSND_sampbuf_ptr = 0; - if (POKEYSND_sampbuf_ptr == POKEYSND_sampbuf_rptr) { - POKEYSND_sampbuf_rptr++; - if (POKEYSND_sampbuf_rptr >= POKEYSND_SAMPBUF_MAX) - POKEYSND_sampbuf_rptr = 0; - } - } - } - -#endif /* VOL_ONLY_SOUND */ - - /* I've disabled any frequencies that exceed the sampling - frequency. There isn't much point in processing frequencies - that the hardware can't reproduce. I've also disabled - processing if the volume is zero. */ - - /* if the channel is volume only */ - /* or the channel is off (volume == 0) */ - /* or the channel freq is greater than the playback freq */ - if ( (POKEY_AUDC[chan + chip_offs] & POKEY_VOL_ONLY) || - ((POKEY_AUDC[chan + chip_offs] & POKEY_VOLUME_MASK) == 0) - || (!BIENIAS_FIX && (Div_n_max[chan + chip_offs] < (Samp_n_max >> 8))) - ) { - /* indicate the channel is 'on' */ - Outvol[chan + chip_offs] = 1; - - /* can only ignore channel if filtering off */ - if ((chan == POKEY_CHAN3 && !(POKEY_AUDCTL[chip] & POKEY_CH1_FILTER)) || - (chan == POKEY_CHAN4 && !(POKEY_AUDCTL[chip] & POKEY_CH2_FILTER)) || - (chan == POKEY_CHAN1) || - (chan == POKEY_CHAN2) - || (!BIENIAS_FIX && (Div_n_max[chan + chip_offs] < (Samp_n_max >> 8))) - ) { - /* and set channel freq to max to reduce processing */ - Div_n_max[chan + chip_offs] = 0x7fffffffL; - Div_n_cnt[chan + chip_offs] = 0x7fffffffL; - } - } - } - } - - /* _enable(); */ /* RSF - removed for portability 31-MAR-97 */ -} - - -/*****************************************************************************/ -/* Module: pokeysnd_process() */ -/* Purpose: To fill the output buffer with the sound output based on the */ -/* pokey chip parameters. */ -/* */ -/* Author: Ron Fries */ -/* Date: January 1, 1997 */ -/* */ -/* Inputs: *buffer - pointer to the buffer where the audio output will */ -/* be placed */ -/* sndn - for mono, size of the playback buffer in samples */ -/* for stereo, size of the playback buffer in left samples */ -/* plus right samples. */ -/* num_pokeys - number of currently active pokeys to process */ -/* */ -/* Outputs: the buffer will be filled with n bytes of audio - no return val */ -/* Also the buffer will be written to disk if Sound recording is ON */ -/* */ -/*****************************************************************************/ - -static void pokeysnd_process_8(void *sndbuffer, int sndn) -{ - register UBYTE *buffer = (UBYTE *) sndbuffer; - register int n = sndn; - - register ULONG *div_n_ptr; - register UBYTE *samp_cnt_w_ptr; - register ULONG event_min; - register UBYTE next_event; -#ifdef CLIP_SOUND - register SWORD cur_val; /* then we have to count as 16-bit signed */ -#ifdef STEREO_SOUND - register SWORD cur_val2; -#endif -#else /* CLIP_SOUND */ - register UBYTE cur_val; /* otherwise we'll simplify as 8-bit unsigned */ -#ifdef STEREO_SOUND - register UBYTE cur_val2; -#endif -#endif /* CLIP_SOUND */ - register UBYTE *out_ptr; - register UBYTE audc; - register UBYTE toggle; - register UBYTE count; - register UBYTE *vol_ptr; - - /* set a pointer to the whole portion of the samp_n_cnt */ -#ifdef WORDS_BIGENDIAN - samp_cnt_w_ptr = ((UBYTE *) (&Samp_n_cnt[0]) + 3); -#else - samp_cnt_w_ptr = ((UBYTE *) (&Samp_n_cnt[0]) + 1); -#endif - - /* set a pointer for optimization */ - out_ptr = Outvol; - vol_ptr = pokeysnd_AUDV; - - /* The current output is pre-determined and then adjusted based on each */ - /* output change for increased performance (less over-all math). */ - /* add the output values of all 4 channels */ - cur_val = POKEYSND_SAMP_MIN; -#ifdef STEREO_SOUND -#ifdef __PLUS - if (POKEYSND_stereo_enabled) -#endif - cur_val2 = POKEYSND_SAMP_MIN; -#endif /* STEREO_SOUND */ - - count = Num_pokeys; - do { - if (*out_ptr++) - cur_val += *vol_ptr; - vol_ptr++; - - if (*out_ptr++) - cur_val += *vol_ptr; - vol_ptr++; - - if (*out_ptr++) - cur_val += *vol_ptr; - vol_ptr++; - - if (*out_ptr++) - cur_val += *vol_ptr; - vol_ptr++; -#ifdef STEREO_SOUND -#ifdef __PLUS - if (POKEYSND_stereo_enabled) -#endif - { - count--; - if (count) { - if (*out_ptr++) - cur_val2 += *vol_ptr; - vol_ptr++; - - if (*out_ptr++) - cur_val2 += *vol_ptr; - vol_ptr++; - - if (*out_ptr++) - cur_val2 += *vol_ptr; - vol_ptr++; - - if (*out_ptr++) - cur_val2 += *vol_ptr; - vol_ptr++; - } - else - break; - } -#endif /* STEREO_SOUND */ - count--; - } while (count); - -#ifdef SYNCHRONIZED_SOUND - cur_val += speaker; -#endif - - /* loop until the buffer is filled */ - while (n) { - /* Normally the routine would simply decrement the 'div by N' */ - /* counters and react when they reach zero. Since we normally */ - /* won't be processing except once every 80 or so counts, */ - /* I've optimized by finding the smallest count and then */ - /* 'accelerated' time by adjusting all pointers by that amount. */ - - /* find next smallest event (either sample or chan 1-4) */ - next_event = POKEY_SAMPLE; - event_min = READ_U32(samp_cnt_w_ptr); - - div_n_ptr = Div_n_cnt; - - count = 0; - do { - /* Though I could have used a loop here, this is faster */ - if (*div_n_ptr <= event_min) { - event_min = *div_n_ptr; - next_event = POKEY_CHAN1 + (count << 2); - } - div_n_ptr++; - if (*div_n_ptr <= event_min) { - event_min = *div_n_ptr; - next_event = POKEY_CHAN2 + (count << 2); - } - div_n_ptr++; - if (*div_n_ptr <= event_min) { - event_min = *div_n_ptr; - next_event = POKEY_CHAN3 + (count << 2); - } - div_n_ptr++; - if (*div_n_ptr <= event_min) { - event_min = *div_n_ptr; - next_event = POKEY_CHAN4 + (count << 2); - } - div_n_ptr++; - - count++; - } while (count < Num_pokeys); - - /* if the next event is a channel change */ - if (next_event != POKEY_SAMPLE) { - /* shift the polynomial counters */ - - count = Num_pokeys; - do { - /* decrement all counters by the smallest count found */ - /* again, no loop for efficiency */ - div_n_ptr--; - *div_n_ptr -= event_min; - div_n_ptr--; - *div_n_ptr -= event_min; - div_n_ptr--; - *div_n_ptr -= event_min; - div_n_ptr--; - *div_n_ptr -= event_min; - - count--; - } while (count); - - - WRITE_U32(samp_cnt_w_ptr, READ_U32(samp_cnt_w_ptr) - event_min); - - /* since the polynomials require a mod (%) function which is - division, I don't adjust the polynomials on the SAMPLE events, - only the CHAN events. I have to keep track of the change, - though. */ - - P4 = (P4 + event_min) % POKEY_POLY4_SIZE; - P5 = (P5 + event_min) % POKEY_POLY5_SIZE; - P9 = (P9 + event_min) % POKEY_POLY9_SIZE; - P17 = (P17 + event_min) % POKEY_POLY17_SIZE; - - /* adjust channel counter */ - Div_n_cnt[next_event] += Div_n_max[next_event]; - - /* get the current AUDC into a register (for optimization) */ - audc = POKEY_AUDC[next_event]; - - /* set a pointer to the current output (for opt...) */ - out_ptr = &Outvol[next_event]; - - /* assume no changes to the output */ - toggle = FALSE; - - /* From here, a good understanding of the hardware is required */ - /* to understand what is happening. I won't be able to provide */ - /* much description to explain it here. */ - - /* if VOLUME only then nothing to process */ - if (!(audc & POKEY_VOL_ONLY)) { - /* if the output is pure or the output is poly5 and the poly5 bit */ - /* is set */ - if ((audc & POKEY_NOTPOLY5) || bit5[P5]) { - /* if the PURETONE bit is set */ - if (audc & POKEY_PURETONE) { - /* then simply toggle the output */ - toggle = TRUE; - } - /* otherwise if POLY4 is selected */ - else if (audc & POKEY_POLY4) { - /* then compare to the poly4 bit */ - toggle = (bit4[P4] == !(*out_ptr)); - } - else { - /* if 9-bit poly is selected on this chip */ - if (POKEY_AUDCTL[next_event >> 2] & POKEY_POLY9) { - /* compare to the poly9 bit */ - toggle = ((POKEY_poly9_lookup[P9] & 1) == !(*out_ptr)); - } - else { - /* otherwise compare to the poly17 bit */ - toggle = (((POKEY_poly17_lookup[P17 >> 3] >> (P17 & 7)) & 1) == !(*out_ptr)); - } - } - } - } - - /* check channel 1 filter (clocked by channel 3) */ - if ( POKEY_AUDCTL[next_event >> 2] & POKEY_CH1_FILTER) { - /* if we're processing channel 3 */ - if ((next_event & 0x03) == POKEY_CHAN3) { - /* check output of channel 1 on same chip */ - if (Outvol[next_event & 0xfd]) { - /* if on, turn it off */ - Outvol[next_event & 0xfd] = 0; -#ifdef STEREO_SOUND -#ifdef __PLUS - if (POKEYSND_stereo_enabled && (next_event & 0x04)) -#else - if ((next_event & 0x04)) -#endif - cur_val2 -= pokeysnd_AUDV[next_event & 0xfd]; - else -#endif /* STEREO_SOUND */ - cur_val -= pokeysnd_AUDV[next_event & 0xfd]; - } - } - } - - /* check channel 2 filter (clocked by channel 4) */ - if ( POKEY_AUDCTL[next_event >> 2] & POKEY_CH2_FILTER) { - /* if we're processing channel 4 */ - if ((next_event & 0x03) == POKEY_CHAN4) { - /* check output of channel 2 on same chip */ - if (Outvol[next_event & 0xfd]) { - /* if on, turn it off */ - Outvol[next_event & 0xfd] = 0; -#ifdef STEREO_SOUND -#ifdef __PLUS - if (POKEYSND_stereo_enabled && (next_event & 0x04)) -#else - if ((next_event & 0x04)) -#endif - cur_val2 -= pokeysnd_AUDV[next_event & 0xfd]; - else -#endif /* STEREO_SOUND */ - cur_val -= pokeysnd_AUDV[next_event & 0xfd]; - } - } - } - - /* if the current output bit has changed */ - if (toggle) { - if (*out_ptr) { - /* remove this channel from the signal */ -#ifdef STEREO_SOUND -#ifdef __PLUS - if (POKEYSND_stereo_enabled && (next_event & 0x04)) -#else - if ((next_event & 0x04)) -#endif - cur_val2 -= pokeysnd_AUDV[next_event]; - else -#endif /* STEREO_SOUND */ - cur_val -= pokeysnd_AUDV[next_event]; - - /* and turn the output off */ - *out_ptr = 0; - } - else { - /* turn the output on */ - *out_ptr = 1; - - /* and add it to the output signal */ -#ifdef STEREO_SOUND -#ifdef __PLUS - if (POKEYSND_stereo_enabled && (next_event & 0x04)) -#else - if ((next_event & 0x04)) -#endif - cur_val2 += pokeysnd_AUDV[next_event]; - else -#endif /* STEREO_SOUND */ - cur_val += pokeysnd_AUDV[next_event]; - } - } - } - else { /* otherwise we're processing a sample */ - /* adjust the sample counter - note we're using the 24.8 integer - which includes an 8 bit fraction for accuracy */ - - int iout; -#ifdef STEREO_SOUND - int iout2; -#endif -#ifdef INTERPOLATE_SOUND - if (cur_val != last_val) { - if (*Samp_n_cnt < Samp_n_max) { /* need interpolation */ - iout = (cur_val * (*Samp_n_cnt) + - last_val * (Samp_n_max - *Samp_n_cnt)) - / Samp_n_max; - } - else - iout = cur_val; - last_val = cur_val; - } - else - iout = cur_val; -#ifdef STEREO_SOUND -#ifdef __PLUS - if (POKEYSND_stereo_enabled) -#endif - if (cur_val2 != last_val2) { - if (*Samp_n_cnt < Samp_n_max) { /* need interpolation */ - iout2 = (cur_val2 * (*Samp_n_cnt) + - last_val2 * (Samp_n_max - *Samp_n_cnt)) - / Samp_n_max; - } - else - iout2 = cur_val2; - last_val2 = cur_val2; - } - else - iout2 = cur_val2; -#endif /* STEREO_SOUND */ -#else /* INTERPOLATE_SOUND */ - iout = cur_val; -#ifdef STEREO_SOUND -#ifdef __PLUS - if (POKEYSND_stereo_enabled) -#endif - iout2 = cur_val2; -#endif /* STEREO_SOUND */ -#endif /* INTERPOLATE_SOUND */ - -#ifdef VOL_ONLY_SOUND -#ifdef __PLUS - if (g_Sound.nDigitized) -#endif - { - if (POKEYSND_sampbuf_rptr != POKEYSND_sampbuf_ptr) { - int l; - if (POKEYSND_sampbuf_cnt[POKEYSND_sampbuf_rptr] > 0) - POKEYSND_sampbuf_cnt[POKEYSND_sampbuf_rptr] -= 1280; - while ((l = POKEYSND_sampbuf_cnt[POKEYSND_sampbuf_rptr]) <= 0) { - POKEYSND_sampout = POKEYSND_sampbuf_val[POKEYSND_sampbuf_rptr]; - POKEYSND_sampbuf_rptr++; - if (POKEYSND_sampbuf_rptr >= POKEYSND_SAMPBUF_MAX) - POKEYSND_sampbuf_rptr = 0; - if (POKEYSND_sampbuf_rptr != POKEYSND_sampbuf_ptr) - POKEYSND_sampbuf_cnt[POKEYSND_sampbuf_rptr] += l; - else - break; - } - } - iout += POKEYSND_sampout; -#ifdef STEREO_SOUND -#ifdef __PLUS - if (POKEYSND_stereo_enabled) -#endif - { - if (sampbuf_rptr2 != sampbuf_ptr2) { - int l; - if (sampbuf_cnt2[sampbuf_rptr2] > 0) - sampbuf_cnt2[sampbuf_rptr2] -= 1280; - while ((l = sampbuf_cnt2[sampbuf_rptr2]) <= 0) { - sampout2 = sampbuf_val2[sampbuf_rptr2]; - sampbuf_rptr2++; - if (sampbuf_rptr2 >= POKEYSND_SAMPBUF_MAX) - sampbuf_rptr2 = 0; - if (sampbuf_rptr2 != sampbuf_ptr2) - sampbuf_cnt2[sampbuf_rptr2] += l; - else - break; - } - } - iout2 += sampout2; - } -#endif /* STEREO_SOUND */ - } -#endif /* VOL_ONLY_SOUND */ - -#ifdef CLIP_SOUND - if (iout > POKEYSND_SAMP_MAX) { /* then check high limit */ - *buffer++ = (UBYTE) POKEYSND_SAMP_MAX; /* and limit if greater */ - } - else if (iout < POKEYSND_SAMP_MIN) { /* else check low limit */ - *buffer++ = (UBYTE) POKEYSND_SAMP_MIN; /* and limit if less */ - } - else { /* otherwise use raw value */ - *buffer++ = (UBYTE) iout; - } -#ifdef STEREO_SOUND -#ifdef __PLUS - if (POKEYSND_stereo_enabled) { - if (iout2 > POKEYSND_SAMP_MAX) - *buffer++ = (UBYTE) POKEYSND_SAMP_MAX; - else if (iout2 < POKEYSND_SAMP_MIN) - *buffer++ = (UBYTE) POKEYSND_SAMP_MIN; - else - *buffer++ = (UBYTE) iout2; - } -#else /* __PLUS */ - if (Num_pokeys > 1) { - if ((POKEYSND_stereo_enabled ? iout2 : iout) > POKEYSND_SAMP_MAX) { /* then check high limit */ - *buffer++ = (UBYTE) POKEYSND_SAMP_MAX; /* and limit if greater */ - } - else if ((POKEYSND_stereo_enabled ? iout2 : iout) < POKEYSND_SAMP_MIN) { /* else check low limit */ - *buffer++ = (UBYTE) POKEYSND_SAMP_MIN; /* and limit if less */ - } - else { /* otherwise use raw value */ - *buffer++ = (UBYTE) (POKEYSND_stereo_enabled ? iout2 : iout); - } - } -#endif /* __PLUS */ -#endif /* STEREO_SOUND */ -#else /* CLIP_SOUND */ - *buffer++ = (UBYTE) iout; /* clipping not selected, use value */ -#ifdef STEREO_SOUND - if (Num_pokeys > 1) -#ifdef ASAP - *buffer++ = (UBYTE) iout2; -#else - *buffer++ = (UBYTE) (POKEYSND_stereo_enabled ? iout2 : iout); -#endif -#endif /* STEREO_SOUND */ -#endif /* CLIP_SOUND */ - -#ifdef WORDS_BIGENDIAN - *(Samp_n_cnt + 1) += Samp_n_max; -#else - *Samp_n_cnt += Samp_n_max; -#endif - /* and indicate one less byte in the buffer */ - n--; -#ifdef STEREO_SOUND -#ifdef __PLUS - if (POKEYSND_stereo_enabled) -#endif - if (Num_pokeys > 1) - n--; -#endif - } - } -#ifdef VOL_ONLY_SOUND -#ifdef __PLUS - if (g_Sound.nDigitized) -#endif - { - if (POKEYSND_sampbuf_rptr == POKEYSND_sampbuf_ptr) - POKEYSND_sampbuf_last = ANTIC_CPU_CLOCK; -#ifdef STEREO_SOUND -#ifdef __PLUS - if (POKEYSND_stereo_enabled) -#endif - if (sampbuf_rptr2 == sampbuf_ptr2) - sampbuf_last2 = ANTIC_CPU_CLOCK; -#endif /* STEREO_SOUND */ - } -#endif /* VOL_ONLY_SOUND */ -} - -#ifdef SERIO_SOUND -static void Update_serio_sound_rf(int out, UBYTE data) -{ -#ifdef VOL_ONLY_SOUND -#ifdef __PLUS - if (g_Sound.nDigitized) { -#endif - int bits, pv, future; - if (!POKEYSND_serio_sound_enabled) return; - - pv = 0; - future = 0; - bits = (data << 1) | 0x200; - while (bits) - { - POKEYSND_sampbuf_lastval -= pv; - pv = (bits & 0x01) * pokeysnd_AUDV[3]; /* FIXME!!! - set volume from AUDV */ - POKEYSND_sampbuf_lastval += pv; - - POKEYSND_sampbuf_val[POKEYSND_sampbuf_ptr] = POKEYSND_sampbuf_lastval; - POKEYSND_sampbuf_cnt[POKEYSND_sampbuf_ptr] = - (ANTIC_CPU_CLOCK + future-POKEYSND_sampbuf_last) * 128 * POKEYSND_samp_freq / 178979; - POKEYSND_sampbuf_last = ANTIC_CPU_CLOCK + future; - POKEYSND_sampbuf_ptr++; - if (POKEYSND_sampbuf_ptr >= POKEYSND_SAMPBUF_MAX ) - POKEYSND_sampbuf_ptr = 0; - if (POKEYSND_sampbuf_ptr == POKEYSND_sampbuf_rptr ) { - POKEYSND_sampbuf_rptr++; - if (POKEYSND_sampbuf_rptr >= POKEYSND_SAMPBUF_MAX) - POKEYSND_sampbuf_rptr = 0; - } - /* 1789790/19200 = 93 */ - future += 93; /* ~ 19200 bit/s - FIXME!!! set speed form AUDF [2] ??? */ - bits >>= 1; - } - POKEYSND_sampbuf_lastval -= pv; -#ifdef __PLUS - } -#endif -#endif /* VOL_ONLY_SOUND */ -} -#endif /* SERIO_SOUND */ - -void POKEYSND_SetVolume(int vol) -{ - if (vol > 100) - vol = 100; - if (vol < 0) - vol = 0; - - POKEYSND_volume = vol * 0x100 / 100; -} - -static void pokeysnd_process_16(void *sndbuffer, int sndn) -{ - UWORD *buffer = (UWORD *) sndbuffer; - int i; - - pokeysnd_process_8(buffer, sndn); - - for (i = sndn - 1; i >= 0; i--) { -#ifndef POKEYSND_SIGNED_SAMPLES - int smp = ((int) (((UBYTE *) buffer)[i]) - 0x80) * POKEYSND_volume; -#else - int smp = ((int) ((SBYTE *) buffer)[i]) * POKEYSND_volume; -#endif - - if (smp > 32767) - smp = 32767; - else if (smp < -32768) - smp = -32768; - - buffer[i] = smp; - } -} - -#ifdef SYNCHRONIZED_SOUND -static void Generate_sync_rf(unsigned int num_ticks) -{ - double new_samp_pos; - unsigned int ticks; - UBYTE *buffer = POKEYSND_process_buffer + POKEYSND_process_buffer_fill; - UBYTE *buffer_end = POKEYSND_process_buffer + POKEYSND_process_buffer_length; - - for (;;) { - double int_part; - new_samp_pos = samp_pos + ticks_per_sample; - new_samp_pos = modf(new_samp_pos, &int_part); - ticks = (unsigned int)int_part; - if (ticks > num_ticks) { - samp_pos -= num_ticks; - break; - } - if (buffer >= buffer_end) - break; - - samp_pos = new_samp_pos; - num_ticks -= ticks; - - if (POKEYSND_snd_flags & POKEYSND_BIT16) { - pokeysnd_process_16(buffer, POKEYSND_num_pokeys); - buffer += 2 * POKEYSND_num_pokeys; - } - else { - pokeysnd_process_8(buffer, POKEYSND_num_pokeys); - buffer += POKEYSND_num_pokeys; - } - - } - - POKEYSND_process_buffer_fill = buffer - POKEYSND_process_buffer; -} -#endif /* SYNCHRONIZED_SOUND */ - -#ifdef CONSOLE_SOUND -void POKEYSND_UpdateConsol(int set) -{ - if (!POKEYSND_console_sound_enabled) - return; -#ifdef SYNCHRONIZED_SOUND - if (set) - Update_synchronized_sound(); -#endif /* SYNCHRONIZED_SOUND */ - POKEYSND_UpdateConsol_ptr(set); -} - -static void Update_consol_sound_rf(int set) -{ -#ifdef SYNCHRONIZED_SOUND - if (set) - speaker = CONSOLE_VOL * GTIA_speaker; -#elif defined(VOL_ONLY_SOUND) - static int prev_atari_speaker = 0; - static unsigned int prev_cpu_clock = 0; - int d; -#ifdef __PLUS - if (!g_Sound.nDigitized) - return; -#endif - - if (!set && POKEYSND_samp_consol_val == 0) - return; - POKEYSND_sampbuf_lastval -= POKEYSND_samp_consol_val; - if (prev_atari_speaker != GTIA_speaker) { - POKEYSND_samp_consol_val = GTIA_speaker * 8 * 4; /* gain */ - prev_cpu_clock = ANTIC_CPU_CLOCK; - } - else if (!set) { - d = ANTIC_CPU_CLOCK - prev_cpu_clock; - if (d < 114) { - POKEYSND_sampbuf_lastval += POKEYSND_samp_consol_val; - return; - } - while (d >= 114 /* CPUL */) { - POKEYSND_samp_consol_val = POKEYSND_samp_consol_val * 99 / 100; - d -= 114; - } - prev_cpu_clock = ANTIC_CPU_CLOCK - d; - } - POKEYSND_sampbuf_lastval += POKEYSND_samp_consol_val; - prev_atari_speaker = GTIA_speaker; - - POKEYSND_sampbuf_val[POKEYSND_sampbuf_ptr] = POKEYSND_sampbuf_lastval; - POKEYSND_sampbuf_cnt[POKEYSND_sampbuf_ptr] = - (ANTIC_CPU_CLOCK - POKEYSND_sampbuf_last) * 128 * POKEYSND_samp_freq / 178979; - POKEYSND_sampbuf_last = ANTIC_CPU_CLOCK; - POKEYSND_sampbuf_ptr++; - if (POKEYSND_sampbuf_ptr >= POKEYSND_SAMPBUF_MAX) - POKEYSND_sampbuf_ptr = 0; - if (POKEYSND_sampbuf_ptr == POKEYSND_sampbuf_rptr) { - POKEYSND_sampbuf_rptr++; - if (POKEYSND_sampbuf_rptr >= POKEYSND_SAMPBUF_MAX) - POKEYSND_sampbuf_rptr = 0; - } -#endif /* !SYNCHRONIZED_SOUND && VOL_ONLY_SOUND */ -} -#endif /* CONSOLE_SOUND */ - -#ifdef VOL_ONLY_SOUND -static void Update_vol_only_sound_rf(void) -{ -#ifdef CONSOLE_SOUND - POKEYSND_UpdateConsol(0); /* mmm */ -#endif /* CONSOLE_SOUND */ -} -#endif /* VOL_ONLY_SOUND */ +/* + * pokeysnd.c - POKEY sound chip emulation, v2.4 + * + * Copyright (C) 1996-1998 Ron Fries + * Copyright (C) 1998-2014 Atari800 development team (see DOC/CREDITS) + * + * This file is part of the Atari800 emulator project which emulates + * the Atari 400, 800, 800XL, 130XE, and 5200 8-bit computers. + * + * Atari800 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 2 of the License, or + * (at your option) any later version. + * + * Atari800 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 Atari800; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include "config.h" +#include +#include + +#ifdef ASAP /* external project, see http://asap.sf.net */ +#include "asap_internal.h" +#else +#include "atari.h" +#ifndef __PLUS +#include "sndsave.h" +#else +#include "sound_win.h" +#endif +#endif +#include "mzpokeysnd.h" +#include "pokeysnd.h" +#if defined(PBI_XLD) || defined (VOICEBOX) +#include "votraxsnd.h" +#endif +#include "antic.h" +#include "gtia.h" +#include "util.h" + +#ifdef WORDS_UNALIGNED_OK +# define READ_U32(x) (*(ULONG *) (x)) +# define WRITE_U32(x, d) (*(ULONG *) (x) = (d)) +#else +# ifdef WORDS_BIGENDIAN +# define READ_U32(x) (((*(unsigned char *)(x)) << 24) | ((*((unsigned char *)(x) + 1)) << 16) | \ + ((*((unsigned char *)(x) + 2)) << 8) | ((*((unsigned char *)(x) + 3)))) +# define WRITE_U32(x, d) \ + { \ + ULONG i = d; \ + (*(unsigned char *) (x)) = (((i) >> 24) & 255); \ + (*((unsigned char *) (x) + 1)) = (((i) >> 16) & 255); \ + (*((unsigned char *) (x) + 2)) = (((i) >> 8) & 255); \ + (*((unsigned char *) (x) + 3)) = ((i) & 255); \ + } +# else +# define READ_U32(x) ((*(unsigned char *) (x)) | ((*((unsigned char *) (x) + 1)) << 8) | \ + ((*((unsigned char *) (x) + 2)) << 16) | ((*((unsigned char *) (x) + 3)) << 24)) +# define WRITE_U32(x, d) \ + { \ + ULONG i = d; \ + (*(unsigned char *)(x)) = ((i) & 255); \ + (*((unsigned char *)(x) + 1)) = (((i) >> 8) & 255); \ + (*((unsigned char *)(x) + 2)) = (((i) >> 16) & 255); \ + (*((unsigned char *)(x) + 3)) = (((i) >> 24) & 255); \ + } +# endif +#endif + +/* GLOBAL VARIABLE DEFINITIONS */ + +/* number of pokey chips currently emulated */ +static UBYTE Num_pokeys; + +static UBYTE pokeysnd_AUDV[4 * POKEY_MAXPOKEYS]; /* Channel volume - derived */ + +static UBYTE Outbit[4 * POKEY_MAXPOKEYS]; /* current state of the output (high or low) */ + +static UBYTE Outvol[4 * POKEY_MAXPOKEYS]; /* last output volume for each channel */ + +/* Initialize the bit patterns for the polynomials. */ + +/* The 4bit and 5bit patterns are the identical ones used in the pokey chip. */ +/* Though the patterns could be packed with 8 bits per byte, using only a */ +/* single bit per byte keeps the math simple, which is important for */ +/* efficient processing. */ + +static UBYTE bit4[POKEY_POLY4_SIZE] = +#ifndef POKEY23_POLY +{1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 1, 1, 0, 1, 0}; /* new table invented by Perry */ +#else +{1, 1, 0, 1, 1, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0}; /* original POKEY 2.3 table */ +#endif + +static UBYTE bit5[POKEY_POLY5_SIZE] = +#ifndef POKEY23_POLY +{1, 1, 1, 1, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0}; +#else +{0, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 0, 0, 1, 0, 1, 0, 1, 1, 0, 1, 1, 1, 0, 1, 0, 0, 0, 0, 0, 1}; +#endif + +static ULONG P4 = 0, /* Global position pointer for the 4-bit POLY array */ + P5 = 0, /* Global position pointer for the 5-bit POLY array */ + P9 = 0, /* Global position pointer for the 9-bit POLY array */ + P17 = 0; /* Global position pointer for the 17-bit POLY array */ + +static ULONG Div_n_cnt[4 * POKEY_MAXPOKEYS], /* Divide by n counter. one for each channel */ + Div_n_max[4 * POKEY_MAXPOKEYS]; /* Divide by n maximum, one for each channel */ + +static ULONG Samp_n_max, /* Sample max. For accuracy, it is *256 */ + Samp_n_cnt[2]; /* Sample cnt. */ + +#ifdef INTERPOLATE_SOUND +static UWORD last_val = 0; /* last output value */ +#ifdef STEREO_SOUND +static UWORD last_val2 = 0; /* last output value */ +#endif +#endif + +/* Volume only emulations declarations */ +#ifdef VOL_ONLY_SOUND + +int POKEYSND_sampbuf_val[POKEYSND_SAMPBUF_MAX]; /* volume values */ +int POKEYSND_sampbuf_cnt[POKEYSND_SAMPBUF_MAX]; /* relative start time */ +int POKEYSND_sampbuf_ptr = 0; /* pointer to sampbuf */ +int POKEYSND_sampbuf_rptr = 0; /* pointer to read from sampbuf */ +int POKEYSND_sampbuf_last = 0; /* last absolute time */ +int POKEYSND_sampbuf_AUDV[4 * POKEY_MAXPOKEYS]; /* prev. channel volume */ +int POKEYSND_sampbuf_lastval = 0; /* last volume */ +int POKEYSND_sampout; /* last out volume */ +int POKEYSND_samp_freq; +int POKEYSND_samp_consol_val = 0; /* actual value of console sound */ +#ifdef STEREO_SOUND +static int sampbuf_val2[POKEYSND_SAMPBUF_MAX]; /* volume values */ +static int sampbuf_cnt2[POKEYSND_SAMPBUF_MAX]; /* relative start time */ +static int sampbuf_ptr2 = 0; /* pointer to sampbuf */ +static int sampbuf_rptr2 = 0; /* pointer to read from sampbuf */ +static int sampbuf_last2 = 0; /* last absolute time */ +static int sampbuf_lastval2 = 0; /* last volume */ +static int sampout2; /* last out volume */ +#endif +#endif /* VOL_ONLY_SOUND */ + +static ULONG snd_freq17 = POKEYSND_FREQ_17_EXACT; +int POKEYSND_playback_freq = 44100; +UBYTE POKEYSND_num_pokeys = 1; +int POKEYSND_snd_flags = 0; +static int mz_quality = 0; /* default quality for mzpokeysnd */ +#ifdef __PLUS +int mz_clear_regs = 0; +#endif + +int POKEYSND_enable_new_pokey = TRUE; +int POKEYSND_bienias_fix = TRUE; /* when TRUE, high frequencies get emulated: better sound but slower */ +#if defined(__PLUS) && !defined(_WX_) +#define BIENIAS_FIX (g_Sound.nBieniasFix) +#else +#define BIENIAS_FIX POKEYSND_bienias_fix +#endif +#ifndef ASAP +#if defined(__LIBRETRO__) +int POKEYSND_stereo_enabled = TRUE; +#else /* __LIBRETRO__ */ +int POKEYSND_stereo_enabled = FALSE; +#endif +#endif + +int POKEYSND_volume = 0x100; + +/* multiple sound engine interface */ +static void pokeysnd_process_8(void *sndbuffer, int sndn); +static void pokeysnd_process_16(void *sndbuffer, int sndn); +static void null_pokey_process(void *sndbuffer, int sndn) {} +void (*POKEYSND_Process_ptr)(void *sndbuffer, int sndn) = null_pokey_process; + +static void Update_pokey_sound_rf(UWORD, UBYTE, UBYTE, UBYTE); +static void null_pokey_sound(UWORD addr, UBYTE val, UBYTE chip, UBYTE gain) {} +void (*POKEYSND_Update_ptr) (UWORD addr, UBYTE val, UBYTE chip, UBYTE gain) + = null_pokey_sound; + +#ifdef SERIO_SOUND +static void Update_serio_sound_rf(int out, UBYTE data); +static void null_serio_sound(int out, UBYTE data) {} +void (*POKEYSND_UpdateSerio)(int out, UBYTE data) = null_serio_sound; +int POKEYSND_serio_sound_enabled = 1; +#endif + +#ifdef CONSOLE_SOUND +static void Update_consol_sound_rf(int set); +static void null_consol_sound(int set) {} +void (*POKEYSND_UpdateConsol_ptr)(int set) = null_consol_sound; +int POKEYSND_console_sound_enabled = 1; +#endif + +#ifdef VOL_ONLY_SOUND +static void Update_vol_only_sound_rf(void); +static void null_vol_only_sound(void) {} +void (*POKEYSND_UpdateVolOnly)(void) = null_vol_only_sound; +#endif + +#ifdef SYNCHRONIZED_SOUND +UBYTE *POKEYSND_process_buffer = NULL; +unsigned int POKEYSND_process_buffer_length; +unsigned int POKEYSND_process_buffer_fill; +static unsigned int prev_update_tick; + +static void Generate_sync_rf(unsigned int num_ticks); +static void null_generate_sync(unsigned int num_ticks) {} +void (*POKEYSND_GenerateSync)(unsigned int num_ticks) = null_generate_sync; + +static double ticks_per_sample; +static double samp_pos; +static int speaker; +static int const CONSOLE_VOL = 32; +#endif /* SYNCHRONIZED_SOUND */ + +/*****************************************************************************/ +/* In my routines, I treat the sample output as another divide by N counter */ +/* For better accuracy, the Samp_n_cnt has a fixed binary decimal point */ +/* which has 8 binary digits to the right of the decimal point. I use a two */ +/* byte array to give me a minimum of 40 bits, and then use pointer math to */ +/* reference either the 24.8 whole/fraction combination or the 32-bit whole */ +/* only number. This is mainly used to keep the math simple for */ +/* optimization. See below: */ +/* */ +/* Representation on little-endian machines: */ +/* xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx | xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx */ +/* fraction whole whole whole whole unused unused unused */ +/* */ +/* Samp_n_cnt[0] gives me a 32-bit int 24 whole bits with 8 fractional bits, */ +/* while (ULONG *)((UBYTE *)(&Samp_n_cnt[0])+1) gives me the 32-bit whole */ +/* number only. */ +/* */ +/* Representation on big-endian machines: */ +/* xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx | xxxxxxxx xxxxxxxx xxxxxxxx.xxxxxxxx */ +/* unused unused unused whole whole whole whole fraction */ +/* */ +/* Samp_n_cnt[1] gives me a 32-bit int 24 whole bits with 8 fractional bits, */ +/* while (ULONG *)((UBYTE *)(&Samp_n_cnt[0])+3) gives me the 32-bit whole */ +/* number only. */ +/*****************************************************************************/ + + +/*****************************************************************************/ +/* Module: pokeysnd_init_rf() */ +/* Purpose: to handle the power-up initialization functions */ +/* these functions should only be executed on a cold-restart */ +/* */ +/* Author: Ron Fries */ +/* Date: January 1, 1997 */ +/* */ +/* Inputs: freq17 - the value for the '1.79MHz' Pokey audio clock */ +/* playback_freq - the playback frequency in samples per second */ +/* num_pokeys - specifies the number of pokey chips to be emulated */ +/* */ +/* Outputs: Adjusts local globals - no return value */ +/* */ +/*****************************************************************************/ + +static int pokeysnd_init_rf(ULONG freq17, int playback_freq, + UBYTE num_pokeys, int flags); + +#ifdef VOL_ONLY_SOUND +/* Initialise variables related to volume-only sound. */ +static void init_vol_only(void) +{ + POKEYSND_sampbuf_rptr = POKEYSND_sampbuf_ptr; + POKEYSND_sampbuf_last = ANTIC_CPU_CLOCK; + POKEYSND_sampbuf_lastval = 0; + POKEYSND_samp_consol_val = 0; +#ifdef STEREO_SOUND + sampbuf_rptr2 = sampbuf_ptr2; + sampbuf_last2 = ANTIC_CPU_CLOCK; + sampbuf_lastval2 = 0; +#endif /* STEREO_SOUND */ +} +#endif /* VOL_ONLY_SOUND */ + +int POKEYSND_DoInit(void) +{ + SndSave_CloseSoundFile(); + +#ifdef VOL_ONLY_SOUND + init_vol_only(); +#endif /* VOL_ONLY_SOUND */ + + if (POKEYSND_enable_new_pokey) + return MZPOKEYSND_Init(snd_freq17, POKEYSND_playback_freq, + POKEYSND_num_pokeys, POKEYSND_snd_flags, mz_quality +#ifdef __PLUS + , mz_clear_regs +#endif + ); + else + return pokeysnd_init_rf(snd_freq17, POKEYSND_playback_freq, + POKEYSND_num_pokeys, POKEYSND_snd_flags); +} + +int POKEYSND_Init(ULONG freq17, int playback_freq, UBYTE num_pokeys, + int flags +#ifdef __PLUS + , int clear_regs +#endif +) +{ + snd_freq17 = freq17; + POKEYSND_playback_freq = playback_freq; + POKEYSND_num_pokeys = num_pokeys; + POKEYSND_snd_flags = flags; +#ifdef __PLUS + mz_clear_regs = clear_regs; +#endif +#ifdef SYNCHRONIZED_SOUND + { + /* A single call to Atari800_Frame may emulate a bit more CPU ticks than the exact number of + ticks per frame (Atari800_tv_mode*114). So we add a few ticks to buffer size just to be safe. */ + unsigned int const surplus_ticks = 10; + double samples_per_frame = (double)POKEYSND_playback_freq/(Atari800_tv_mode == Atari800_TV_PAL ? Atari800_FPS_PAL : Atari800_FPS_NTSC); + unsigned int ticks_per_frame = Atari800_tv_mode*114; + unsigned int max_ticks_per_frame = ticks_per_frame + surplus_ticks; + double ticks_per_sample = (double)ticks_per_frame / samples_per_frame; + POKEYSND_process_buffer_length = POKEYSND_num_pokeys * (unsigned int)ceil((double)max_ticks_per_frame / ticks_per_sample) * ((POKEYSND_snd_flags & POKEYSND_BIT16) ? 2:1); + free(POKEYSND_process_buffer); + POKEYSND_process_buffer = (UBYTE *)Util_malloc(POKEYSND_process_buffer_length); + POKEYSND_process_buffer_fill = 0; + prev_update_tick = ANTIC_CPU_CLOCK; + } +#endif /* SYNCHRONIZED_SOUND */ + +#if defined(PBI_XLD) || defined (VOICEBOX) + VOTRAXSND_Init(playback_freq, num_pokeys, (flags & POKEYSND_BIT16)); +#endif + return POKEYSND_DoInit(); +} + +void POKEYSND_SetMzQuality(int quality) /* specially for win32, perhaps not needed? */ +{ + mz_quality = quality; +} + +void POKEYSND_Process(void *sndbuffer, int sndn) +{ + POKEYSND_Process_ptr(sndbuffer, sndn); +#if defined(PBI_XLD) || defined (VOICEBOX) + VOTRAXSND_Process(sndbuffer,sndn); +#endif +#if !defined(__PLUS) && !defined(ASAP) + SndSave_WriteToSoundFile((const unsigned char *)sndbuffer, sndn); +#endif +} + +#ifdef SYNCHRONIZED_SOUND +static void Update_synchronized_sound(void) +{ + POKEYSND_GenerateSync(ANTIC_CPU_CLOCK - prev_update_tick); + prev_update_tick = ANTIC_CPU_CLOCK; +} + +int POKEYSND_UpdateProcessBuffer(void) +{ + int sndn; + Update_synchronized_sound(); + sndn = POKEYSND_process_buffer_fill / ((POKEYSND_snd_flags & POKEYSND_BIT16) ? 2 : 1); + POKEYSND_process_buffer_fill = 0; + +#if defined(PBI_XLD) || defined (VOICEBOX) + VOTRAXSND_Process(POKEYSND_process_buffer, sndn); +#endif +#if !defined(__PLUS) && !defined(ASAP) + SndSave_WriteToSoundFile((const unsigned char *)POKEYSND_process_buffer, sndn); +#endif + return sndn; +} +#endif /* SYNCHRONIZED_SOUND */ + +#ifdef SYNCHRONIZED_SOUND +static void init_syncsound(void) +{ + double samples_per_frame = (double)POKEYSND_playback_freq/(Atari800_tv_mode == Atari800_TV_PAL ? Atari800_FPS_PAL : Atari800_FPS_NTSC); + unsigned int ticks_per_frame = Atari800_tv_mode*114; + ticks_per_sample = (double)ticks_per_frame / samples_per_frame; + samp_pos = 0.0; + POKEYSND_GenerateSync = Generate_sync_rf; + speaker = 0; +} +#endif /* SYNCHRONIZED_SOUND */ + +static int pokeysnd_init_rf(ULONG freq17, int playback_freq, + UBYTE num_pokeys, int flags) +{ + UBYTE chan; + + POKEYSND_Update_ptr = Update_pokey_sound_rf; +#ifdef SERIO_SOUND + POKEYSND_UpdateSerio = Update_serio_sound_rf; +#endif +#ifdef CONSOLE_SOUND + POKEYSND_UpdateConsol_ptr = Update_consol_sound_rf; +#endif +#ifdef VOL_ONLY_SOUND + POKEYSND_UpdateVolOnly = Update_vol_only_sound_rf; +#endif + + POKEYSND_Process_ptr = (flags & POKEYSND_BIT16) ? pokeysnd_process_16 : pokeysnd_process_8; + +#ifdef VOL_ONLY_SOUND + POKEYSND_samp_freq = playback_freq; +#endif + + /* start all of the polynomial counters at zero */ + P4 = 0; + P5 = 0; + P9 = 0; + P17 = 0; + + /* calculate the sample 'divide by N' value based on the playback freq. */ + Samp_n_max = ((ULONG) freq17 << 8) / playback_freq; + + Samp_n_cnt[0] = 0; /* initialize all bits of the sample */ + Samp_n_cnt[1] = 0; /* 'divide by N' counter */ + + for (chan = 0; chan < (POKEY_MAXPOKEYS * 4); chan++) { + Outvol[chan] = 0; + Outbit[chan] = 0; + Div_n_cnt[chan] = 0; + Div_n_max[chan] = 0x7fffffffL; + pokeysnd_AUDV[chan] = 0; +#ifdef VOL_ONLY_SOUND + POKEYSND_sampbuf_AUDV[chan] = 0; +#endif + } + + /* set the number of pokey chips currently emulated */ + Num_pokeys = num_pokeys; + +#ifdef SYNCHRONIZED_SOUND + init_syncsound(); +#endif + return 0; /* OK */ +} + + +/*****************************************************************************/ +/* Module: Update_pokey_sound_rf() */ +/* Purpose: To process the latest control values stored in the AUDF, AUDC, */ +/* and AUDCTL registers. It pre-calculates as much information as */ +/* possible for better performance. This routine has not been */ +/* optimized. */ +/* */ +/* Author: Ron Fries */ +/* Date: January 1, 1997 */ +/* */ +/* Inputs: addr - the address of the parameter to be changed */ +/* val - the new value to be placed in the specified address */ +/* gain - specified as an 8-bit fixed point number - use 1 for no */ +/* amplification (output is multiplied by gain) */ +/* */ +/* Outputs: Adjusts local globals - no return value */ +/* */ +/*****************************************************************************/ + +void POKEYSND_Update(UWORD addr, UBYTE val, UBYTE chip, UBYTE gain) +{ +#ifdef SYNCHRONIZED_SOUND + Update_synchronized_sound(); +#endif /* SYNCHRONIZED_SOUND */ + POKEYSND_Update_ptr(addr, val, chip, gain); +} + +static void Update_pokey_sound_rf(UWORD addr, UBYTE val, UBYTE chip, + UBYTE gain) +{ + ULONG new_val = 0; + UBYTE chan; + UBYTE chan_mask; + UBYTE chip_offs; + + /* calculate the chip_offs for the channel arrays */ + chip_offs = chip << 2; + + /* determine which address was changed */ + switch (addr & 0x0f) { + case POKEY_OFFSET_AUDF1: + /* POKEY_AUDF[POKEY_CHAN1 + chip_offs] = val; */ + chan_mask = 1 << POKEY_CHAN1; + if (POKEY_AUDCTL[chip] & POKEY_CH1_CH2) /* if ch 1&2 tied together */ + chan_mask |= 1 << POKEY_CHAN2; /* then also change on ch2 */ + break; + case POKEY_OFFSET_AUDC1: + /* POKEY_AUDC[POKEY_CHAN1 + chip_offs] = val; */ + pokeysnd_AUDV[POKEY_CHAN1 + chip_offs] = (val & POKEY_VOLUME_MASK) * gain; + chan_mask = 1 << POKEY_CHAN1; + break; + case POKEY_OFFSET_AUDF2: + /* POKEY_AUDF[POKEY_CHAN2 + chip_offs] = val; */ + chan_mask = 1 << POKEY_CHAN2; + break; + case POKEY_OFFSET_AUDC2: + /* POKEY_AUDC[POKEY_CHAN2 + chip_offs] = val; */ + pokeysnd_AUDV[POKEY_CHAN2 + chip_offs] = (val & POKEY_VOLUME_MASK) * gain; + chan_mask = 1 << POKEY_CHAN2; + break; + case POKEY_OFFSET_AUDF3: + /* POKEY_AUDF[POKEY_CHAN3 + chip_offs] = val; */ + chan_mask = 1 << POKEY_CHAN3; + if (POKEY_AUDCTL[chip] & POKEY_CH3_CH4) /* if ch 3&4 tied together */ + chan_mask |= 1 << POKEY_CHAN4; /* then also change on ch4 */ + break; + case POKEY_OFFSET_AUDC3: + /* POKEY_AUDC[POKEY_CHAN3 + chip_offs] = val; */ + pokeysnd_AUDV[POKEY_CHAN3 + chip_offs] = (val & POKEY_VOLUME_MASK) * gain; + chan_mask = 1 << POKEY_CHAN3; + break; + case POKEY_OFFSET_AUDF4: + /* POKEY_AUDF[POKEY_CHAN4 + chip_offs] = val; */ + chan_mask = 1 << POKEY_CHAN4; + break; + case POKEY_OFFSET_AUDC4: + /* POKEY_AUDC[POKEY_CHAN4 + chip_offs] = val; */ + pokeysnd_AUDV[POKEY_CHAN4 + chip_offs] = (val & POKEY_VOLUME_MASK) * gain; + chan_mask = 1 << POKEY_CHAN4; + break; + case POKEY_OFFSET_AUDCTL: + /* POKEY_AUDCTL[chip] = val; */ + chan_mask = 15; /* all channels */ + break; + default: + chan_mask = 0; + break; + } + + /************************************************************/ + /* As defined in the manual, the exact Div_n_cnt values are */ + /* different depending on the frequency and resolution: */ + /* 64 kHz or 15 kHz - AUDF + 1 */ + /* 1 MHz, 8-bit - AUDF + 4 */ + /* 1 MHz, 16-bit - POKEY_AUDF[POKEY_CHAN1]+256*POKEY_AUDF[POKEY_CHAN2] + 7 */ + /************************************************************/ + + /* only reset the channels that have changed */ + + if (chan_mask & (1 << POKEY_CHAN1)) { + /* process channel 1 frequency */ + if (POKEY_AUDCTL[chip] & POKEY_CH1_179) + new_val = POKEY_AUDF[POKEY_CHAN1 + chip_offs] + 4; + else + new_val = (POKEY_AUDF[POKEY_CHAN1 + chip_offs] + 1) * POKEY_Base_mult[chip]; + + if (new_val != Div_n_max[POKEY_CHAN1 + chip_offs]) { + Div_n_max[POKEY_CHAN1 + chip_offs] = new_val; + + if (Div_n_cnt[POKEY_CHAN1 + chip_offs] > new_val) { + Div_n_cnt[POKEY_CHAN1 + chip_offs] = new_val; + } + } + } + + if (chan_mask & (1 << POKEY_CHAN2)) { + /* process channel 2 frequency */ + if (POKEY_AUDCTL[chip] & POKEY_CH1_CH2) { + if (POKEY_AUDCTL[chip] & POKEY_CH1_179) + new_val = POKEY_AUDF[POKEY_CHAN2 + chip_offs] * 256 + + POKEY_AUDF[POKEY_CHAN1 + chip_offs] + 7; + else + new_val = (POKEY_AUDF[POKEY_CHAN2 + chip_offs] * 256 + + POKEY_AUDF[POKEY_CHAN1 + chip_offs] + 1) * POKEY_Base_mult[chip]; + } + else + new_val = (POKEY_AUDF[POKEY_CHAN2 + chip_offs] + 1) * POKEY_Base_mult[chip]; + + if (new_val != Div_n_max[POKEY_CHAN2 + chip_offs]) { + Div_n_max[POKEY_CHAN2 + chip_offs] = new_val; + + if (Div_n_cnt[POKEY_CHAN2 + chip_offs] > new_val) { + Div_n_cnt[POKEY_CHAN2 + chip_offs] = new_val; + } + } + } + + if (chan_mask & (1 << POKEY_CHAN3)) { + /* process channel 3 frequency */ + if (POKEY_AUDCTL[chip] & POKEY_CH3_179) + new_val = POKEY_AUDF[POKEY_CHAN3 + chip_offs] + 4; + else + new_val = (POKEY_AUDF[POKEY_CHAN3 + chip_offs] + 1) * POKEY_Base_mult[chip]; + + if (new_val != Div_n_max[POKEY_CHAN3 + chip_offs]) { + Div_n_max[POKEY_CHAN3 + chip_offs] = new_val; + + if (Div_n_cnt[POKEY_CHAN3 + chip_offs] > new_val) { + Div_n_cnt[POKEY_CHAN3 + chip_offs] = new_val; + } + } + } + + if (chan_mask & (1 << POKEY_CHAN4)) { + /* process channel 4 frequency */ + if (POKEY_AUDCTL[chip] & POKEY_CH3_CH4) { + if (POKEY_AUDCTL[chip] & POKEY_CH3_179) + new_val = POKEY_AUDF[POKEY_CHAN4 + chip_offs] * 256 + + POKEY_AUDF[POKEY_CHAN3 + chip_offs] + 7; + else + new_val = (POKEY_AUDF[POKEY_CHAN4 + chip_offs] * 256 + + POKEY_AUDF[POKEY_CHAN3 + chip_offs] + 1) * POKEY_Base_mult[chip]; + } + else + new_val = (POKEY_AUDF[POKEY_CHAN4 + chip_offs] + 1) * POKEY_Base_mult[chip]; + + if (new_val != Div_n_max[POKEY_CHAN4 + chip_offs]) { + Div_n_max[POKEY_CHAN4 + chip_offs] = new_val; + + if (Div_n_cnt[POKEY_CHAN4 + chip_offs] > new_val) { + Div_n_cnt[POKEY_CHAN4 + chip_offs] = new_val; + } + } + } + + /* if channel is volume only, set current output */ + for (chan = POKEY_CHAN1; chan <= POKEY_CHAN4; chan++) { + if (chan_mask & (1 << chan)) { + +#ifdef VOL_ONLY_SOUND + +#ifdef __PLUS + if (g_Sound.nDigitized) +#endif + if ((POKEY_AUDC[chan + chip_offs] & POKEY_VOL_ONLY)) { + +#ifdef STEREO_SOUND + +#ifdef __PLUS + if (POKEYSND_stereo_enabled && chip & 0x01) +#else + if (chip & 0x01) +#endif + { + sampbuf_lastval2 += pokeysnd_AUDV[chan + chip_offs] + - POKEYSND_sampbuf_AUDV[chan + chip_offs]; + + sampbuf_val2[sampbuf_ptr2] = sampbuf_lastval2; + POKEYSND_sampbuf_AUDV[chan + chip_offs] = pokeysnd_AUDV[chan + chip_offs]; + sampbuf_cnt2[sampbuf_ptr2] = + (ANTIC_CPU_CLOCK - sampbuf_last2) * 128 * POKEYSND_samp_freq / 178979; + sampbuf_last2 = ANTIC_CPU_CLOCK; + sampbuf_ptr2++; + if (sampbuf_ptr2 >= POKEYSND_SAMPBUF_MAX) + sampbuf_ptr2 = 0; + if (sampbuf_ptr2 == sampbuf_rptr2) { + sampbuf_rptr2++; + if (sampbuf_rptr2 >= POKEYSND_SAMPBUF_MAX) + sampbuf_rptr2 = 0; + } + } + else +#endif /* STEREO_SOUND */ + { + POKEYSND_sampbuf_lastval += pokeysnd_AUDV[chan + chip_offs] + -POKEYSND_sampbuf_AUDV[chan + chip_offs]; + + POKEYSND_sampbuf_val[POKEYSND_sampbuf_ptr] = POKEYSND_sampbuf_lastval; + POKEYSND_sampbuf_AUDV[chan + chip_offs] = pokeysnd_AUDV[chan + chip_offs]; + POKEYSND_sampbuf_cnt[POKEYSND_sampbuf_ptr] = + (ANTIC_CPU_CLOCK - POKEYSND_sampbuf_last) * 128 * POKEYSND_samp_freq / 178979; + POKEYSND_sampbuf_last = ANTIC_CPU_CLOCK; + POKEYSND_sampbuf_ptr++; + if (POKEYSND_sampbuf_ptr >= POKEYSND_SAMPBUF_MAX) + POKEYSND_sampbuf_ptr = 0; + if (POKEYSND_sampbuf_ptr == POKEYSND_sampbuf_rptr) { + POKEYSND_sampbuf_rptr++; + if (POKEYSND_sampbuf_rptr >= POKEYSND_SAMPBUF_MAX) + POKEYSND_sampbuf_rptr = 0; + } + } + } + +#endif /* VOL_ONLY_SOUND */ + + /* I've disabled any frequencies that exceed the sampling + frequency. There isn't much point in processing frequencies + that the hardware can't reproduce. I've also disabled + processing if the volume is zero. */ + + /* if the channel is volume only */ + /* or the channel is off (volume == 0) */ + /* or the channel freq is greater than the playback freq */ + if ( (POKEY_AUDC[chan + chip_offs] & POKEY_VOL_ONLY) || + ((POKEY_AUDC[chan + chip_offs] & POKEY_VOLUME_MASK) == 0) + || (!BIENIAS_FIX && (Div_n_max[chan + chip_offs] < (Samp_n_max >> 8))) + ) { + /* indicate the channel is 'on' */ + Outvol[chan + chip_offs] = 1; + + /* can only ignore channel if filtering off */ + if ((chan == POKEY_CHAN3 && !(POKEY_AUDCTL[chip] & POKEY_CH1_FILTER)) || + (chan == POKEY_CHAN4 && !(POKEY_AUDCTL[chip] & POKEY_CH2_FILTER)) || + (chan == POKEY_CHAN1) || + (chan == POKEY_CHAN2) + || (!BIENIAS_FIX && (Div_n_max[chan + chip_offs] < (Samp_n_max >> 8))) + ) { + /* and set channel freq to max to reduce processing */ + Div_n_max[chan + chip_offs] = 0x7fffffffL; + Div_n_cnt[chan + chip_offs] = 0x7fffffffL; + } + } + } + } + + /* _enable(); */ /* RSF - removed for portability 31-MAR-97 */ +} + + +/*****************************************************************************/ +/* Module: pokeysnd_process() */ +/* Purpose: To fill the output buffer with the sound output based on the */ +/* pokey chip parameters. */ +/* */ +/* Author: Ron Fries */ +/* Date: January 1, 1997 */ +/* */ +/* Inputs: *buffer - pointer to the buffer where the audio output will */ +/* be placed */ +/* sndn - for mono, size of the playback buffer in samples */ +/* for stereo, size of the playback buffer in left samples */ +/* plus right samples. */ +/* num_pokeys - number of currently active pokeys to process */ +/* */ +/* Outputs: the buffer will be filled with n bytes of audio - no return val */ +/* Also the buffer will be written to disk if Sound recording is ON */ +/* */ +/*****************************************************************************/ + +static void pokeysnd_process_8(void *sndbuffer, int sndn) +{ + register UBYTE *buffer = (UBYTE *) sndbuffer; + register int n = sndn; + + register ULONG *div_n_ptr; + register UBYTE *samp_cnt_w_ptr; + register ULONG event_min; + register UBYTE next_event; +#ifdef CLIP_SOUND + register SWORD cur_val; /* then we have to count as 16-bit signed */ +#ifdef STEREO_SOUND + register SWORD cur_val2; +#endif +#else /* CLIP_SOUND */ + register UBYTE cur_val; /* otherwise we'll simplify as 8-bit unsigned */ +#ifdef STEREO_SOUND + register UBYTE cur_val2; +#endif +#endif /* CLIP_SOUND */ + register UBYTE *out_ptr; + register UBYTE audc; + register UBYTE toggle; + register UBYTE count; + register UBYTE *vol_ptr; + + /* set a pointer to the whole portion of the samp_n_cnt */ +#ifdef WORDS_BIGENDIAN + samp_cnt_w_ptr = ((UBYTE *) (&Samp_n_cnt[0]) + 3); +#else + samp_cnt_w_ptr = ((UBYTE *) (&Samp_n_cnt[0]) + 1); +#endif + + /* set a pointer for optimization */ + out_ptr = Outvol; + vol_ptr = pokeysnd_AUDV; + + /* The current output is pre-determined and then adjusted based on each */ + /* output change for increased performance (less over-all math). */ + /* add the output values of all 4 channels */ + cur_val = POKEYSND_SAMP_MIN; +#ifdef STEREO_SOUND +#ifdef __PLUS + if (POKEYSND_stereo_enabled) +#endif + cur_val2 = POKEYSND_SAMP_MIN; +#endif /* STEREO_SOUND */ + + count = Num_pokeys; + do { + if (*out_ptr++) + cur_val += *vol_ptr; + vol_ptr++; + + if (*out_ptr++) + cur_val += *vol_ptr; + vol_ptr++; + + if (*out_ptr++) + cur_val += *vol_ptr; + vol_ptr++; + + if (*out_ptr++) + cur_val += *vol_ptr; + vol_ptr++; +#ifdef STEREO_SOUND +#ifdef __PLUS + if (POKEYSND_stereo_enabled) +#endif + { + count--; + if (count) { + if (*out_ptr++) + cur_val2 += *vol_ptr; + vol_ptr++; + + if (*out_ptr++) + cur_val2 += *vol_ptr; + vol_ptr++; + + if (*out_ptr++) + cur_val2 += *vol_ptr; + vol_ptr++; + + if (*out_ptr++) + cur_val2 += *vol_ptr; + vol_ptr++; + } + else + break; + } +#endif /* STEREO_SOUND */ + count--; + } while (count); + +#ifdef SYNCHRONIZED_SOUND + cur_val += speaker; +#endif + + /* loop until the buffer is filled */ + while (n) { + /* Normally the routine would simply decrement the 'div by N' */ + /* counters and react when they reach zero. Since we normally */ + /* won't be processing except once every 80 or so counts, */ + /* I've optimized by finding the smallest count and then */ + /* 'accelerated' time by adjusting all pointers by that amount. */ + + /* find next smallest event (either sample or chan 1-4) */ + next_event = POKEY_SAMPLE; + event_min = READ_U32(samp_cnt_w_ptr); + + div_n_ptr = Div_n_cnt; + + count = 0; + do { + /* Though I could have used a loop here, this is faster */ + if (*div_n_ptr <= event_min) { + event_min = *div_n_ptr; + next_event = POKEY_CHAN1 + (count << 2); + } + div_n_ptr++; + if (*div_n_ptr <= event_min) { + event_min = *div_n_ptr; + next_event = POKEY_CHAN2 + (count << 2); + } + div_n_ptr++; + if (*div_n_ptr <= event_min) { + event_min = *div_n_ptr; + next_event = POKEY_CHAN3 + (count << 2); + } + div_n_ptr++; + if (*div_n_ptr <= event_min) { + event_min = *div_n_ptr; + next_event = POKEY_CHAN4 + (count << 2); + } + div_n_ptr++; + + count++; + } while (count < Num_pokeys); + + /* if the next event is a channel change */ + if (next_event != POKEY_SAMPLE) { + /* shift the polynomial counters */ + + count = Num_pokeys; + do { + /* decrement all counters by the smallest count found */ + /* again, no loop for efficiency */ + div_n_ptr--; + *div_n_ptr -= event_min; + div_n_ptr--; + *div_n_ptr -= event_min; + div_n_ptr--; + *div_n_ptr -= event_min; + div_n_ptr--; + *div_n_ptr -= event_min; + + count--; + } while (count); + + + WRITE_U32(samp_cnt_w_ptr, READ_U32(samp_cnt_w_ptr) - event_min); + + /* since the polynomials require a mod (%) function which is + division, I don't adjust the polynomials on the SAMPLE events, + only the CHAN events. I have to keep track of the change, + though. */ + + P4 = (P4 + event_min) % POKEY_POLY4_SIZE; + P5 = (P5 + event_min) % POKEY_POLY5_SIZE; + P9 = (P9 + event_min) % POKEY_POLY9_SIZE; + P17 = (P17 + event_min) % POKEY_POLY17_SIZE; + + /* adjust channel counter */ + Div_n_cnt[next_event] += Div_n_max[next_event]; + + /* get the current AUDC into a register (for optimization) */ + audc = POKEY_AUDC[next_event]; + + /* set a pointer to the current output (for opt...) */ + out_ptr = &Outvol[next_event]; + + /* assume no changes to the output */ + toggle = FALSE; + + /* From here, a good understanding of the hardware is required */ + /* to understand what is happening. I won't be able to provide */ + /* much description to explain it here. */ + + /* if VOLUME only then nothing to process */ + if (!(audc & POKEY_VOL_ONLY)) { + /* if the output is pure or the output is poly5 and the poly5 bit */ + /* is set */ + if ((audc & POKEY_NOTPOLY5) || bit5[P5]) { + /* if the PURETONE bit is set */ + if (audc & POKEY_PURETONE) { + /* then simply toggle the output */ + toggle = TRUE; + } + /* otherwise if POLY4 is selected */ + else if (audc & POKEY_POLY4) { + /* then compare to the poly4 bit */ + toggle = (bit4[P4] == !(*out_ptr)); + } + else { + /* if 9-bit poly is selected on this chip */ + if (POKEY_AUDCTL[next_event >> 2] & POKEY_POLY9) { + /* compare to the poly9 bit */ + toggle = ((POKEY_poly9_lookup[P9] & 1) == !(*out_ptr)); + } + else { + /* otherwise compare to the poly17 bit */ + toggle = (((POKEY_poly17_lookup[P17 >> 3] >> (P17 & 7)) & 1) == !(*out_ptr)); + } + } + } + } + + /* check channel 1 filter (clocked by channel 3) */ + if ( POKEY_AUDCTL[next_event >> 2] & POKEY_CH1_FILTER) { + /* if we're processing channel 3 */ + if ((next_event & 0x03) == POKEY_CHAN3) { + /* check output of channel 1 on same chip */ + if (Outvol[next_event & 0xfd]) { + /* if on, turn it off */ + Outvol[next_event & 0xfd] = 0; +#ifdef STEREO_SOUND +#ifdef __PLUS + if (POKEYSND_stereo_enabled && (next_event & 0x04)) +#else + if ((next_event & 0x04)) +#endif + cur_val2 -= pokeysnd_AUDV[next_event & 0xfd]; + else +#endif /* STEREO_SOUND */ + cur_val -= pokeysnd_AUDV[next_event & 0xfd]; + } + } + } + + /* check channel 2 filter (clocked by channel 4) */ + if ( POKEY_AUDCTL[next_event >> 2] & POKEY_CH2_FILTER) { + /* if we're processing channel 4 */ + if ((next_event & 0x03) == POKEY_CHAN4) { + /* check output of channel 2 on same chip */ + if (Outvol[next_event & 0xfd]) { + /* if on, turn it off */ + Outvol[next_event & 0xfd] = 0; +#ifdef STEREO_SOUND +#ifdef __PLUS + if (POKEYSND_stereo_enabled && (next_event & 0x04)) +#else + if ((next_event & 0x04)) +#endif + cur_val2 -= pokeysnd_AUDV[next_event & 0xfd]; + else +#endif /* STEREO_SOUND */ + cur_val -= pokeysnd_AUDV[next_event & 0xfd]; + } + } + } + + /* if the current output bit has changed */ + if (toggle) { + if (*out_ptr) { + /* remove this channel from the signal */ +#ifdef STEREO_SOUND +#ifdef __PLUS + if (POKEYSND_stereo_enabled && (next_event & 0x04)) +#else + if ((next_event & 0x04)) +#endif + cur_val2 -= pokeysnd_AUDV[next_event]; + else +#endif /* STEREO_SOUND */ + cur_val -= pokeysnd_AUDV[next_event]; + + /* and turn the output off */ + *out_ptr = 0; + } + else { + /* turn the output on */ + *out_ptr = 1; + + /* and add it to the output signal */ +#ifdef STEREO_SOUND +#ifdef __PLUS + if (POKEYSND_stereo_enabled && (next_event & 0x04)) +#else + if ((next_event & 0x04)) +#endif + cur_val2 += pokeysnd_AUDV[next_event]; + else +#endif /* STEREO_SOUND */ + cur_val += pokeysnd_AUDV[next_event]; + } + } + } + else { /* otherwise we're processing a sample */ + /* adjust the sample counter - note we're using the 24.8 integer + which includes an 8 bit fraction for accuracy */ + + int iout; +#ifdef STEREO_SOUND + int iout2; +#endif +#ifdef INTERPOLATE_SOUND + if (cur_val != last_val) { + if (*Samp_n_cnt < Samp_n_max) { /* need interpolation */ + iout = (cur_val * (*Samp_n_cnt) + + last_val * (Samp_n_max - *Samp_n_cnt)) + / Samp_n_max; + } + else + iout = cur_val; + last_val = cur_val; + } + else + iout = cur_val; +#ifdef STEREO_SOUND +#ifdef __PLUS + if (POKEYSND_stereo_enabled) +#endif + if (cur_val2 != last_val2) { + if (*Samp_n_cnt < Samp_n_max) { /* need interpolation */ + iout2 = (cur_val2 * (*Samp_n_cnt) + + last_val2 * (Samp_n_max - *Samp_n_cnt)) + / Samp_n_max; + } + else + iout2 = cur_val2; + last_val2 = cur_val2; + } + else + iout2 = cur_val2; +#endif /* STEREO_SOUND */ +#else /* INTERPOLATE_SOUND */ + iout = cur_val; +#ifdef STEREO_SOUND +#ifdef __PLUS + if (POKEYSND_stereo_enabled) +#endif + iout2 = cur_val2; +#endif /* STEREO_SOUND */ +#endif /* INTERPOLATE_SOUND */ + +#ifdef VOL_ONLY_SOUND +#ifdef __PLUS + if (g_Sound.nDigitized) +#endif + { + if (POKEYSND_sampbuf_rptr != POKEYSND_sampbuf_ptr) { + int l; + if (POKEYSND_sampbuf_cnt[POKEYSND_sampbuf_rptr] > 0) + POKEYSND_sampbuf_cnt[POKEYSND_sampbuf_rptr] -= 1280; + while ((l = POKEYSND_sampbuf_cnt[POKEYSND_sampbuf_rptr]) <= 0) { + POKEYSND_sampout = POKEYSND_sampbuf_val[POKEYSND_sampbuf_rptr]; + POKEYSND_sampbuf_rptr++; + if (POKEYSND_sampbuf_rptr >= POKEYSND_SAMPBUF_MAX) + POKEYSND_sampbuf_rptr = 0; + if (POKEYSND_sampbuf_rptr != POKEYSND_sampbuf_ptr) + POKEYSND_sampbuf_cnt[POKEYSND_sampbuf_rptr] += l; + else + break; + } + } + iout += POKEYSND_sampout; +#ifdef STEREO_SOUND +#ifdef __PLUS + if (POKEYSND_stereo_enabled) +#endif + { + if (sampbuf_rptr2 != sampbuf_ptr2) { + int l; + if (sampbuf_cnt2[sampbuf_rptr2] > 0) + sampbuf_cnt2[sampbuf_rptr2] -= 1280; + while ((l = sampbuf_cnt2[sampbuf_rptr2]) <= 0) { + sampout2 = sampbuf_val2[sampbuf_rptr2]; + sampbuf_rptr2++; + if (sampbuf_rptr2 >= POKEYSND_SAMPBUF_MAX) + sampbuf_rptr2 = 0; + if (sampbuf_rptr2 != sampbuf_ptr2) + sampbuf_cnt2[sampbuf_rptr2] += l; + else + break; + } + } + iout2 += sampout2; + } +#endif /* STEREO_SOUND */ + } +#endif /* VOL_ONLY_SOUND */ + +#ifdef CLIP_SOUND + if (iout > POKEYSND_SAMP_MAX) { /* then check high limit */ + *buffer++ = (UBYTE) POKEYSND_SAMP_MAX; /* and limit if greater */ + } + else if (iout < POKEYSND_SAMP_MIN) { /* else check low limit */ + *buffer++ = (UBYTE) POKEYSND_SAMP_MIN; /* and limit if less */ + } + else { /* otherwise use raw value */ + *buffer++ = (UBYTE) iout; + } +#ifdef STEREO_SOUND +#ifdef __PLUS + if (POKEYSND_stereo_enabled) { + if (iout2 > POKEYSND_SAMP_MAX) + *buffer++ = (UBYTE) POKEYSND_SAMP_MAX; + else if (iout2 < POKEYSND_SAMP_MIN) + *buffer++ = (UBYTE) POKEYSND_SAMP_MIN; + else + *buffer++ = (UBYTE) iout2; + } +#else /* __PLUS */ + if (Num_pokeys > 1) { + if ((POKEYSND_stereo_enabled ? iout2 : iout) > POKEYSND_SAMP_MAX) { /* then check high limit */ + *buffer++ = (UBYTE) POKEYSND_SAMP_MAX; /* and limit if greater */ + } + else if ((POKEYSND_stereo_enabled ? iout2 : iout) < POKEYSND_SAMP_MIN) { /* else check low limit */ + *buffer++ = (UBYTE) POKEYSND_SAMP_MIN; /* and limit if less */ + } + else { /* otherwise use raw value */ + *buffer++ = (UBYTE) (POKEYSND_stereo_enabled ? iout2 : iout); + } + } +#endif /* __PLUS */ +#endif /* STEREO_SOUND */ +#else /* CLIP_SOUND */ + *buffer++ = (UBYTE) iout; /* clipping not selected, use value */ +#ifdef STEREO_SOUND + if (Num_pokeys > 1) +#ifdef ASAP + *buffer++ = (UBYTE) iout2; +#else + *buffer++ = (UBYTE) (POKEYSND_stereo_enabled ? iout2 : iout); +#endif +#endif /* STEREO_SOUND */ +#endif /* CLIP_SOUND */ + +#ifdef WORDS_BIGENDIAN + *(Samp_n_cnt + 1) += Samp_n_max; +#else + *Samp_n_cnt += Samp_n_max; +#endif + /* and indicate one less byte in the buffer */ + n--; +#ifdef STEREO_SOUND +#ifdef __PLUS + if (POKEYSND_stereo_enabled) +#endif + if (Num_pokeys > 1) + n--; +#endif + } + } +#ifdef VOL_ONLY_SOUND +#ifdef __PLUS + if (g_Sound.nDigitized) +#endif + { + if (POKEYSND_sampbuf_rptr == POKEYSND_sampbuf_ptr) + POKEYSND_sampbuf_last = ANTIC_CPU_CLOCK; +#ifdef STEREO_SOUND +#ifdef __PLUS + if (POKEYSND_stereo_enabled) +#endif + if (sampbuf_rptr2 == sampbuf_ptr2) + sampbuf_last2 = ANTIC_CPU_CLOCK; +#endif /* STEREO_SOUND */ + } +#endif /* VOL_ONLY_SOUND */ +} + +#ifdef SERIO_SOUND +static void Update_serio_sound_rf(int out, UBYTE data) +{ +#ifdef VOL_ONLY_SOUND +#ifdef __PLUS + if (g_Sound.nDigitized) { +#endif + int bits, pv, future; + if (!POKEYSND_serio_sound_enabled) return; + + pv = 0; + future = 0; + bits = (data << 1) | 0x200; + while (bits) + { + POKEYSND_sampbuf_lastval -= pv; + pv = (bits & 0x01) * pokeysnd_AUDV[3]; /* FIXME!!! - set volume from AUDV */ + POKEYSND_sampbuf_lastval += pv; + + POKEYSND_sampbuf_val[POKEYSND_sampbuf_ptr] = POKEYSND_sampbuf_lastval; + POKEYSND_sampbuf_cnt[POKEYSND_sampbuf_ptr] = + (ANTIC_CPU_CLOCK + future-POKEYSND_sampbuf_last) * 128 * POKEYSND_samp_freq / 178979; + POKEYSND_sampbuf_last = ANTIC_CPU_CLOCK + future; + POKEYSND_sampbuf_ptr++; + if (POKEYSND_sampbuf_ptr >= POKEYSND_SAMPBUF_MAX ) + POKEYSND_sampbuf_ptr = 0; + if (POKEYSND_sampbuf_ptr == POKEYSND_sampbuf_rptr ) { + POKEYSND_sampbuf_rptr++; + if (POKEYSND_sampbuf_rptr >= POKEYSND_SAMPBUF_MAX) + POKEYSND_sampbuf_rptr = 0; + } + /* 1789790/19200 = 93 */ + future += 93; /* ~ 19200 bit/s - FIXME!!! set speed form AUDF [2] ??? */ + bits >>= 1; + } + POKEYSND_sampbuf_lastval -= pv; +#ifdef __PLUS + } +#endif +#endif /* VOL_ONLY_SOUND */ +} +#endif /* SERIO_SOUND */ + +void POKEYSND_SetVolume(int vol) +{ + if (vol > 100) + vol = 100; + if (vol < 0) + vol = 0; + + POKEYSND_volume = vol * 0x100 / 100; +} + +static void pokeysnd_process_16(void *sndbuffer, int sndn) +{ + UWORD *buffer = (UWORD *) sndbuffer; + int i; + + pokeysnd_process_8(buffer, sndn); + + for (i = sndn - 1; i >= 0; i--) { +#ifndef POKEYSND_SIGNED_SAMPLES + int smp = ((int) (((UBYTE *) buffer)[i]) - 0x80) * POKEYSND_volume; +#else + int smp = ((int) ((SBYTE *) buffer)[i]) * POKEYSND_volume; +#endif + + if (smp > 32767) + smp = 32767; + else if (smp < -32768) + smp = -32768; + + buffer[i] = smp; + } +} + +#ifdef SYNCHRONIZED_SOUND +static void Generate_sync_rf(unsigned int num_ticks) +{ + double new_samp_pos; + unsigned int ticks; + UBYTE *buffer = POKEYSND_process_buffer + POKEYSND_process_buffer_fill; + UBYTE *buffer_end = POKEYSND_process_buffer + POKEYSND_process_buffer_length; + + for (;;) { + double int_part; + new_samp_pos = samp_pos + ticks_per_sample; + new_samp_pos = modf(new_samp_pos, &int_part); + ticks = (unsigned int)int_part; + if (ticks > num_ticks) { + samp_pos -= num_ticks; + break; + } + if (buffer >= buffer_end) + break; + + samp_pos = new_samp_pos; + num_ticks -= ticks; + + if (POKEYSND_snd_flags & POKEYSND_BIT16) { + pokeysnd_process_16(buffer, POKEYSND_num_pokeys); + buffer += 2 * POKEYSND_num_pokeys; + } + else { + pokeysnd_process_8(buffer, POKEYSND_num_pokeys); + buffer += POKEYSND_num_pokeys; + } + + } + + POKEYSND_process_buffer_fill = buffer - POKEYSND_process_buffer; +} +#endif /* SYNCHRONIZED_SOUND */ + +#ifdef CONSOLE_SOUND +void POKEYSND_UpdateConsol(int set) +{ + if (!POKEYSND_console_sound_enabled) + return; +#ifdef SYNCHRONIZED_SOUND + if (set) + Update_synchronized_sound(); +#endif /* SYNCHRONIZED_SOUND */ + POKEYSND_UpdateConsol_ptr(set); +} + +static void Update_consol_sound_rf(int set) +{ +#ifdef SYNCHRONIZED_SOUND + if (set) + speaker = CONSOLE_VOL * GTIA_speaker; +#elif defined(VOL_ONLY_SOUND) + static int prev_atari_speaker = 0; + static unsigned int prev_cpu_clock = 0; + int d; +#ifdef __PLUS + if (!g_Sound.nDigitized) + return; +#endif + + if (!set && POKEYSND_samp_consol_val == 0) + return; + POKEYSND_sampbuf_lastval -= POKEYSND_samp_consol_val; + if (prev_atari_speaker != GTIA_speaker) { + POKEYSND_samp_consol_val = GTIA_speaker * 8 * 4; /* gain */ + prev_cpu_clock = ANTIC_CPU_CLOCK; + } + else if (!set) { + d = ANTIC_CPU_CLOCK - prev_cpu_clock; + if (d < 114) { + POKEYSND_sampbuf_lastval += POKEYSND_samp_consol_val; + return; + } + while (d >= 114 /* CPUL */) { + POKEYSND_samp_consol_val = POKEYSND_samp_consol_val * 99 / 100; + d -= 114; + } + prev_cpu_clock = ANTIC_CPU_CLOCK - d; + } + POKEYSND_sampbuf_lastval += POKEYSND_samp_consol_val; + prev_atari_speaker = GTIA_speaker; + + POKEYSND_sampbuf_val[POKEYSND_sampbuf_ptr] = POKEYSND_sampbuf_lastval; + POKEYSND_sampbuf_cnt[POKEYSND_sampbuf_ptr] = + (ANTIC_CPU_CLOCK - POKEYSND_sampbuf_last) * 128 * POKEYSND_samp_freq / 178979; + POKEYSND_sampbuf_last = ANTIC_CPU_CLOCK; + POKEYSND_sampbuf_ptr++; + if (POKEYSND_sampbuf_ptr >= POKEYSND_SAMPBUF_MAX) + POKEYSND_sampbuf_ptr = 0; + if (POKEYSND_sampbuf_ptr == POKEYSND_sampbuf_rptr) { + POKEYSND_sampbuf_rptr++; + if (POKEYSND_sampbuf_rptr >= POKEYSND_SAMPBUF_MAX) + POKEYSND_sampbuf_rptr = 0; + } +#endif /* !SYNCHRONIZED_SOUND && VOL_ONLY_SOUND */ +} +#endif /* CONSOLE_SOUND */ + +#ifdef VOL_ONLY_SOUND +static void Update_vol_only_sound_rf(void) +{ +#ifdef CONSOLE_SOUND + POKEYSND_UpdateConsol(0); /* mmm */ +#endif /* CONSOLE_SOUND */ +} +#endif /* VOL_ONLY_SOUND */ diff --git a/atari800/src/sio.c b/atari800/src/sio.c index cefe3d6..daf1781 100644 --- a/atari800/src/sio.c +++ b/atari800/src/sio.c @@ -1678,6 +1678,48 @@ int SIO_RotateDisks(void) #ifndef BASIC +#if defined(__LIBRETRO__) +void Retro_SIO_StateSave(void) +{ + int i; + + for (i = 0; i < 8; i++) { + Retro_SaveINT((int*)&SIO_drive_status[i], 1); + Retro_SaveFNAME(SIO_filename[i]); + } +} + +void Retro_SIO_StateRead(void) +{ + int i; + + for (i = 0; i < 8; i++) { + int saved_drive_status; + char filename[FILENAME_MAX]; + + Retro_ReadINT(&saved_drive_status, 1); + SIO_drive_status[i] = (SIO_UnitStatus)saved_drive_status; + + Retro_ReadFNAME(filename); + if (filename[0] == 0) + continue; + + /* If the disk drive wasn't empty or off when saved, + mount the disk */ + switch (saved_drive_status) { + case SIO_READ_ONLY: + SIO_Mount(i + 1, filename, TRUE); + break; + case SIO_READ_WRITE: + SIO_Mount(i + 1, filename, FALSE); + break; + default: + break; + } + } +} +#endif /* __LIBRETRO__ */ + void SIO_StateSave(void) { int i; diff --git a/atari800/src/sio.h b/atari800/src/sio.h index be6c439..a4e03e1 100644 --- a/atari800/src/sio.h +++ b/atari800/src/sio.h @@ -59,4 +59,9 @@ int SIO_WriteSector(int unit, int sector, const UBYTE *buffer); void SIO_StateSave(void); void SIO_StateRead(void); +#if defined(__LIBRETRO__) +void Retro_SIO_StateSave(void); +void Retro_SIO_StateRead(void); +#endif + #endif /* SIO_H_ */ diff --git a/atari800/src/sound.c b/atari800/src/sound.c index 722a089..6196268 100644 --- a/atari800/src/sound.c +++ b/atari800/src/sound.c @@ -231,7 +231,9 @@ int Sound_Setup(void) return FALSE; } +#ifndef __LIBRETRO__ POKEYSND_stereo_enabled = Sound_out.channels == 2; +#endif /* __LIBRETRO__ */ #ifndef SOUND_CALLBACK free(process_buffer); process_buffer_size = Sound_out.buffer_frames * Sound_out.channels * Sound_out.sample_size; diff --git a/atari800/src/statesav.c b/atari800/src/statesav.c index a6603fd..76a6932 100644 --- a/atari800/src/statesav.c +++ b/atari800/src/statesav.c @@ -26,6 +26,14 @@ #include #include #include + +#if defined(__LIBRETRO__) +#include +#include + +int Retro_SaveState_Size = 0; +#endif + #ifdef HAVE_ERRNO_H #include #endif @@ -99,6 +107,12 @@ static size_t mem_write(const void *buf, size_t len, gzFile *stream); #define Z_OK 0 #endif + +#if defined(__LIBRETRO__) +static memstream_t* state_stream = NULL; +static bool state_stream_error = false; +#endif + static gzFile StateFile = NULL; static int nFileError = Z_OK; @@ -120,6 +134,119 @@ static void GetGZErrorText(void) Log_print("State file I/O failed."); } +#if defined(__LIBRETRO__) +/* Value is memory location of data, num is number of type to save */ +void Retro_SaveUBYTE(const UBYTE* data, int num) +{ + if (!state_stream || state_stream_error) + return; + + /* Assumption is that UBYTE = 8bits and the pointer passed in refers + * directly to the active bits in a padded location. If not (unlikely) + * you'll have to redefine this to save appropriately for cross-platform + * compatibility */ + if (memstream_write(state_stream, data, num) != num) + state_stream_error = true; + + Retro_SaveState_Size += num; +} + +void Retro_SaveINT(const int* data, int num) +{ + if (!state_stream || state_stream_error) + return; + + /* INTs are always saved as 32bits (4 bytes) in the file. They can be any size + * on the platform however. The sign bit is clobbered into the fourth byte saved + * for each int; on read it will be extended out to its proper position for the + * native INT size */ + while (num > 0) + { + UBYTE signbit = 0; + unsigned int temp; + UBYTE byte; + int temp0 = *data++; + if (temp0 < 0) + { + temp0 = -temp0; + signbit = 0x80; + } + temp = (unsigned int)temp0; + + byte = temp & 0xff; + if (memstream_write(state_stream, &byte, 1) != 1) + { + state_stream_error = true; + break; + } + + temp >>= 8; + byte = temp & 0xff; + if (memstream_write(state_stream, &byte, 1) != 1) + { + state_stream_error = true; + break; + } + + temp >>= 8; + byte = temp & 0xff; + if (memstream_write(state_stream, &byte, 1) != 1) + { + state_stream_error = true; + break; + } + + temp >>= 8; + byte = (temp & 0x7f) | signbit; + if (memstream_write(state_stream, &byte, 1) != 1) + { + state_stream_error = true; + break; + } + + num--; + Retro_SaveState_Size += 4; + } + +} + +/* Value is memory location of data, num is number of type to save */ +void Retro_SaveUWORD(const UWORD* data, int num) +{ + if (!state_stream || state_stream_error) + return; + + /* UWORDS are saved as 16bits, regardless of the size on this particular + * platform. Each byte of the UWORD will be pushed out individually in + * LSB order. The shifts here and in the read routines will work for both + * LSB and MSB architectures. */ + while (num > 0) + { + UWORD temp = *data++; + UBYTE byte = temp & 0xff; + + if (memstream_write(state_stream, &byte, 1) != 1) + { + state_stream_error = true; + break; + } + + temp >>= 8; + byte = temp & 0xff; + + if (memstream_write(state_stream, &byte, 1) != 1) + { + state_stream_error = true; + break; + } + + num--; + Retro_SaveState_Size += 2; + } + +} +#endif + /* Value is memory location of data, num is number of type to save */ void StateSav_SaveUBYTE(const UBYTE *data, int num) { @@ -328,10 +455,354 @@ void StateSav_ReadFNAME(char *filename) filename[namelen] = 0; } +#if defined(__LIBRETRO__) + +int Retro_SaveAtariState(uint8_t* data, size_t size, UBYTE SaveVerbose) +{ + UBYTE StateVersion = SAVE_VERSION_NUMBER; + Retro_SaveState_Size = 0; + + /* Clean up any existing memory stream */ + if (state_stream) + { + memstream_close(state_stream); + memstream_set_buffer(NULL, 0); + state_stream = NULL; + } + state_stream_error = false; + + if (!data || size < 1) + goto error; + + /* Open memory stream */ + memstream_set_buffer(data, size); + state_stream = memstream_open(1); + if (!state_stream) + goto error; + + if (memstream_write(state_stream, "ATARI800", 8) != 8) + goto error; + + Retro_SaveState_Size += 8; + + Retro_SaveUBYTE(&StateVersion, 1); + Retro_SaveUBYTE(&SaveVerbose, 1); + /* The order here is important. Atari800_StateSave must be first because it saves the machine type, and + decisions on what to save/not save are made based off that later in the process */ + Retro_Atari800_StateSave(); + Retro_CARTRIDGE_StateSave(); + Retro_SIO_StateSave(); + Retro_ANTIC_StateSave(); + Retro_CPU_StateSave(SaveVerbose); + Retro_GTIA_StateSave(); + Retro_PIA_StateSave(); + Retro_POKEY_StateSave(); +#ifdef XEP80_EMULATION + //XEP80_StateSave(); +#else + { + int local_xep80_enabled = FALSE; + Retro_SaveINT(&local_xep80_enabled, 1); + } +#endif /* XEP80_EMULATION */ + PBI_StateSave(); +#ifdef PBI_MIO + //PBI_MIO_StateSave(); +#else + { + int local_mio_enabled = FALSE; + Retro_SaveINT(&local_mio_enabled, 1); + } +#endif /* PBI_MIO */ +#ifdef PBI_BB + //PBI_BB_StateSave(); +#else + { + int local_bb_enabled = FALSE; + Retro_SaveINT(&local_bb_enabled, 1); + } +#endif /* PBI_BB */ +#ifdef PBI_XLD + //PBI_XLD_StateSave(); +#else + { + int local_xld_enabled = FALSE; + Retro_SaveINT(&local_xld_enabled, 1); + } +#endif /* PBI_XLD */ +#ifdef DREAMCAST + DCStateSave(); +#endif + + /* Close memory stream */ + memstream_close(state_stream); + memstream_set_buffer(NULL, 0); + state_stream = NULL; + + if (state_stream_error) + return FALSE; + + return Retro_SaveState_Size; + +error: + if (state_stream) + memstream_close(state_stream); + memstream_set_buffer(NULL, 0); + state_stream = NULL; + state_stream_error = true; + return FALSE; +} + +void Retro_SaveFNAME(const char* filename) +{ + UWORD namelen; +#ifdef HAVE_GETCWD + char dirname[FILENAME_MAX] = ""; + + /* Check to see if file is in application tree, if so, just save as + relative path....*/ + if (getcwd(dirname, FILENAME_MAX) != NULL) { + if (strncmp(filename, dirname, strlen(dirname)) == 0) + /* XXX: check if '/' or '\\' follows dirname in filename? */ + filename += strlen(dirname) + 1; + } +#endif + + namelen = strlen(filename); + /* Save the length of the filename, followed by the filename */ + Retro_SaveUWORD(&namelen, 1); + Retro_SaveUBYTE((const UBYTE*)filename, namelen); +} + +int Retro_ReadAtariState(const uint8_t* data, size_t size) +{ + char header_string[8]; + UBYTE StateVersion = 0; /* The version of the save file */ + UBYTE SaveVerbose = 0; /* Verbose mode means save basic, OS if patched */ + + Retro_SaveState_Size = 0; + + /* Clean up any existing memory stream */ + if (state_stream) + { + memstream_close(state_stream); + memstream_set_buffer(NULL, 0); + state_stream = NULL; + } + state_stream_error = false; + + /* Open memory stream */ + memstream_set_buffer((uint8_t*)data, size); + state_stream = memstream_open(0); + if (!state_stream) + goto error; + + if (memstream_read(state_stream, header_string, 8) != 8) + goto error; + + Retro_SaveState_Size += 8; + + if (memcmp(header_string, "ATARI800", 8) != 0) + goto error; + + if ((memstream_read(state_stream, &StateVersion, 1) != 1) || + (memstream_read(state_stream, &SaveVerbose, 1) != 1)) + goto error; + + Retro_SaveState_Size += 2; + + if (StateVersion > SAVE_VERSION_NUMBER || StateVersion < 3) + goto error; + + Retro_Atari800_StateRead(StateVersion); + if (StateVersion >= 4) { + Retro_CARTRIDGE_StateRead(StateVersion); + Retro_SIO_StateRead(); + } + Retro_ANTIC_StateRead(); + Retro_CPU_StateRead(SaveVerbose, StateVersion); + Retro_GTIA_StateRead(StateVersion); + Retro_PIA_StateRead(StateVersion); + Retro_POKEY_StateRead(); + if (StateVersion >= 6) { +#ifdef XEP80_EMULATION + //XEP80_StateRead(); +#else + int local_xep80_enabled; + Retro_ReadINT(&local_xep80_enabled, 1); + if (local_xep80_enabled) + goto error; +#endif /* XEP80_EMULATION */ + PBI_StateRead(); +#ifdef PBI_MIO + //PBI_MIO_StateRead(); +#else + { + int local_mio_enabled; + Retro_ReadINT(&local_mio_enabled, 1); + if (local_mio_enabled) + goto error; + } +#endif /* PBI_MIO */ +#ifdef PBI_BB + //PBI_BB_StateRead(); +#else + { + int local_bb_enabled; + Retro_ReadINT(&local_bb_enabled, 1); + if (local_bb_enabled) + goto error; + } +#endif /* PBI_BB */ +#ifdef PBI_XLD + //PBI_XLD_StateRead(); +#else + { + int local_xld_enabled; + Retro_ReadINT(&local_xld_enabled, 1); + if (local_xld_enabled) + goto error; + } +#endif /* PBI_XLD */ + } +#ifdef DREAMCAST + DCStateRead(); +#endif + + /* Close memory stream */ + memstream_close(state_stream); + memstream_set_buffer(NULL, 0); + state_stream = NULL; + + if (state_stream_error) + return FALSE; + + GTIA_consol_override = 0; + + return Retro_SaveState_Size; + +error: + if (state_stream) + memstream_close(state_stream); + memstream_set_buffer(NULL, 0); + state_stream = NULL; + state_stream_error = true; + return FALSE; +} + +/* Value is memory location of data, num is number of type to save */ +void Retro_ReadUBYTE(UBYTE* data, int num) +{ + if (!state_stream || state_stream_error) + return; + + if (memstream_read(state_stream, data, num) != num) + state_stream_error = true; + + Retro_SaveState_Size += num; +} + +/* Value is memory location of data, num is number of type to save */ +void Retro_ReadUWORD(UWORD* data, int num) +{ + if (!state_stream || state_stream_error) + return; + + while (num > 0) + { + UBYTE byte1; + UBYTE byte2; + + if (memstream_read(state_stream, &byte1, 1) != 1) + { + state_stream_error = true; + break; + } + + if (memstream_read(state_stream, &byte2, 1) != 1) + { + state_stream_error = true; + break; + } + + *data++ = (byte2 << 8) | byte1; + num--; + + Retro_SaveState_Size += 2; + } + +} + +void Retro_ReadINT(int* data, int num) +{ + if (!state_stream || state_stream_error) + return; + + while (num > 0) + { + UBYTE signbit = 0; + int temp; + UBYTE byte1; + UBYTE byte2; + UBYTE byte3; + UBYTE byte4; + + if (memstream_read(state_stream, &byte1, 1) != 1) + { + state_stream_error = true; + break; + } + + if (memstream_read(state_stream, &byte2, 1) != 1) + { + state_stream_error = true; + break; + } + + if (memstream_read(state_stream, &byte3, 1) != 1) + { + state_stream_error = true; + break; + } + + if (memstream_read(state_stream, &byte4, 1) != 1) + { + state_stream_error = true; + break; + } + + signbit = byte4 & 0x80; + byte4 &= 0x7f; + + temp = (byte4 << 24) | (byte3 << 16) | (byte2 << 8) | byte1; + if (signbit) + temp = -temp; + *data++ = temp; + + num--; + + Retro_SaveState_Size += 4; + } +} + +void Retro_ReadFNAME(char* filename) +{ + UWORD namelen = 0; + + Retro_ReadUWORD(&namelen, 1); + if (namelen >= FILENAME_MAX) + return; + + Retro_ReadUBYTE((UBYTE*)filename, namelen); + filename[namelen] = 0; +} +#endif /* __RETROLIB__ */ + int StateSav_SaveAtariState(const char *filename, const char *mode, UBYTE SaveVerbose) { UBYTE StateVersion = SAVE_VERSION_NUMBER; + if (StateFile != NULL) { GZCLOSE(StateFile); StateFile = NULL; diff --git a/atari800/src/statesav.h b/atari800/src/statesav.h index 2c6b692..eb4240d 100644 --- a/atari800/src/statesav.h +++ b/atari800/src/statesav.h @@ -16,4 +16,19 @@ void StateSav_ReadUWORD(UWORD *data, int num); void StateSav_ReadINT(int *data, int num); void StateSav_ReadFNAME(char *filename); +#if defined(__LIBRETRO__) +int Retro_SaveAtariState(uint8_t* data, size_t size, UBYTE SaveVerbose); +int Retro_ReadAtariState(const uint8_t* data, size_t size); + +void Retro_SaveUBYTE(const UBYTE* data, int num); +void Retro_SaveUWORD(const UWORD* data, int num); +void Retro_SaveINT(const int* data, int num); +void Retro_SaveFNAME(const char* filename); + +void Retro_ReadUBYTE(UBYTE* data, int num); +void Retro_ReadUWORD(UWORD* data, int num); +void Retro_ReadINT(int* data, int num); +void Retro_ReadFNAME(char* filename); +#endif + #endif /* STATESAV_H_ */ diff --git a/atari800/src/sysrom.c b/atari800/src/sysrom.c index b3a01ee..f56c658 100644 --- a/atari800/src/sysrom.c +++ b/atari800/src/sysrom.c @@ -32,6 +32,10 @@ #include #include +#ifdef HAVE_SYS_STAT_H +#include +#endif + #include "sysrom.h" #include "cfg.h" @@ -266,6 +270,9 @@ int SYSROM_FindInDir(char const *directory, int only_if_not_set) { DIR *dir; struct dirent *entry; +#ifdef HAVE_STAT + struct stat status; +#endif if (only_if_not_set && num_unset_roms == 0) /* No unset ROM paths left. */ @@ -281,7 +288,25 @@ int SYSROM_FindInDir(char const *directory, int only_if_not_set) int id; ULONG crc; int matched_crc = FALSE; + + if (entry->d_name[0] == '.') { + /* never match "." */ + if (entry->d_name[1] == '\0') + continue; + /* never match ".." */ + if (entry->d_name[1] == '.' && entry->d_name[2] == '\0') + continue; + } + Util_catpath(full_filename, directory, entry->d_name); + +#ifdef HAVE_STAT + if (stat(full_filename, &status) == 0) { + if (S_ISDIR(status.st_mode)) + continue ; + } +#endif + if ((file = fopen(full_filename, "rb")) == NULL) /* Ignore non-readable files (e.g. directories). */ continue; diff --git a/atari800/src/ui.c b/atari800/src/ui.c index 3d46138..f6be44b 100644 --- a/atari800/src/ui.c +++ b/atari800/src/ui.c @@ -1048,6 +1048,11 @@ int UI_SelectCartType(int k) UI_MENU_ACTION(CARTRIDGE_THECART_32M, CARTRIDGE_THECART_32M_DESC), UI_MENU_ACTION(CARTRIDGE_THECART_64M, CARTRIDGE_THECART_64M_DESC), UI_MENU_ACTION(CARTRIDGE_XEGS_8F_64, CARTRIDGE_XEGS_8F_64_DESC), + UI_MENU_ACTION(CARTRIDGE_5200_SUPER_64, CARTRIDGE_5200_SUPER_64_DESC), + UI_MENU_ACTION(CARTRIDGE_5200_SUPER_128, CARTRIDGE_5200_SUPER_128_DESC), + UI_MENU_ACTION(CARTRIDGE_5200_SUPER_256, CARTRIDGE_5200_SUPER_256_DESC), + UI_MENU_ACTION(CARTRIDGE_5200_SUPER_512, CARTRIDGE_5200_SUPER_512_DESC), + UI_MENU_ACTION(CARTRIDGE_ATMAX_NEW_1024, CARTRIDGE_ATMAX_NEW_1024_DESC), UI_MENU_END }; diff --git a/atari800_libretro.info b/atari800_libretro.info new file mode 100644 index 0000000..92de5c1 --- /dev/null +++ b/atari800_libretro.info @@ -0,0 +1,53 @@ +# Software Information +display_name = "Atari - 5200 (Atari800)" +authors = "Petr Stehlik" +supported_extensions = "xfd|atr|dcm|cas|bin|a52|zip|atx|car|rom|com|xex|m3u" +corename = "Atari800" +categories = "Emulator" +license = "GPLv2" +permissions = "" + +# Hardware Information +manufacturer = "Atari" +systemname = "Atari 5200" +systemid = "atari_5200" +database = "Atari - 5200" +display_version = "3.1.0" + +# Libretro Features +supports_no_game = "false" +savestate = "true" +savestate_features = "null" +cheats = "false" +input_descriptors = "true" +memory_descriptors = "false" +libretro_saves = "false" +core_options = "true" +core_options_version = "1.0" +load_subsystem = "false" +hw_render = "false" +needs_fullpath = "true" +disk_control = "false" +is_experimental = "false" +needs_kbd_mouse_focus = "true" + +# BIOS/Firmware +firmware_count = 5 +firmware0_desc = "5200.rom (Atari 5200 BIOS)" +firmware0_path = "5200.rom" +firmware0_opt = "false" +firmware1_desc = "ATARIBAS.ROM (Atari BASIC)" +firmware1_path = "ATARIBAS.ROM" +firmware1_opt = "false" +firmware2_desc = "ATARIOSA.ROM (Atari 400/800 OS A)" +firmware2_path = "ATARIOSA.ROM" +firmware2_opt = "false" +firmware3_desc = "ATARIOSB.ROM (Atari 400/800 OS B)" +firmware3_path = "ATARIOSB.ROM" +firmware3_opt = "false" +firmware4_desc = "ATARIXL.ROM (Atari XL/XE OS)" +firmware4_path = "ATARIXL.ROM" +firmware4_opt = "false" +notes = "(!) 5200.rom (md5): 281f20ea4320404ec820fb7ec0693b38|(!) ATARIBAS.ROM (md5): 0bac0c6a50104045d902df4503a4c30b|(!) ATARIOSA.ROM (md5): eb1f32f5d9f382db1bbfb8d7f9cb343a|(!) ATARIOSB.ROM (md5): a3e8d617c95d08031fe1b20d541434b2|(!) ATARIXL.ROM (md5): 06daac977823773a3eea3422fd26a703" + +description = "A port of the free and portable Atari800 emulator to libretro. This core supports games and programs written for the Atari 8-bit computers (400, 800, 600 XL, 800XL, 130XE) and 5200 console. When loaded, the core should boot to the Atari Computer - Memo Pad screen and will generate a .atari800.cfg config file in the frontend's home directory and will add the required BIOS files it detects in the frontend's system directory to the config file. Once that is done, users may manually select which Atari system to emulate through the Atari System core option. These and other options can also be modified through the core's own menu, accessible through the retrokeyboard's F1 key." diff --git a/libretro/atari5200_hash.h b/libretro/atari5200_hash.h index 84a9037..4704096 100644 --- a/libretro/atari5200_hash.h +++ b/libretro/atari5200_hash.h @@ -1,6 +1,10 @@ -#define a5200 0 -#define a5200_40 1 -#define a5200_ee_16 2 +#define a5200 0 // 4, 8, NS16, 32 +#define a5200_40 1 // Bounty Bob +#define a5200_ee_16 2 // 2 16k eeproms +#define a5200_64 3 // supercarts +#define a5200_128 4 +#define a5200_256 5 +#define a5200_512 6 typedef struct { int type; @@ -9,114 +13,289 @@ typedef struct { ULONG crc; } a5200_rom; -a5200_rom a5200_game[]={ +a5200_rom a5200_game[] = { { a5200,"5200menu.bin",8192,0x0de2db48}, - { a5200,"aep.bin",16384,0x35484751}, - { a5200,"decathln.bin",16384,0xf43e7cd0}, - { a5200,"asteroid.bin",8192,0x38480891}, - { a5200_ee_16,"astrchse.bin",16384,0x4019ecec}, - { a5200,"ballblze.bin",32768,0x94d97d14}, - { a5200_ee_16,"bzone.bin",16384,0xb3b8e314}, - { a5200,"beamrid.bin",16384,0x9bae58dc}, - { a5200,"berzerk.bin",16384,0xbe3cd348}, - { a5200,"blckbelt.bin",32768,0xed47b0d8}, - { a5200,"blaster.bin",16384,0xc8f9c094}, - { a5200,"blueprnt.bin",16384,0x0624e6e7}, - { a5200_40,"bbstrksb.bin",40960,0x7873c6dd}, - { a5200_ee_16,"buckrog.bin",16384,0x04807705}, - { a5200_ee_16,"centiped.bin",16384,0x536a70fe}, - { a5200,"choplift.bin",16384,0x9ad53bbc}, - { a5200_ee_16,"congo.bin",16384,0xf1f42bbd}, - { a5200_ee_16,"cntrmsre.bin",16384,0xfd541c80}, - { a5200,"pitfall2.bin",16384,0x4b910461}, - { a5200_ee_16,"defender.bin",16384,0xbd52623b}, - { a5200_ee_16,"digdug.bin",16384,0x6a687f9c}, - { a5200,"dredfctr.bin",8192,0x460def2d}, - { a5200,"finalleg.bin",16384,0xd3bd3221}, + { a5200,"A.E. (Proto)",16384,0x35484751}, + { a5200,"Activision Decathlon",16384,0xf43e7cd0}, + { a5200,"Asteroids (USA) (Proto)",8192,0x38480891}, + { a5200_ee_16,"Astro Chase",16384,0x4019ecec}, + { a5200,"Ballblazer",32768,0x94d97d14}, + { a5200,"Ballblazer", 8192, 0xDEF2A207 }, + { a5200_ee_16,"Battle Zone (Proto)",16384,0xb3b8e314}, + { a5200,"Beamrider",16384,0x9bae58dc}, + { a5200,"Berzerk",16384,0xbe3cd348}, + { a5200,"Blackbelt (Proto)",32768,0xed47b0d8}, + { a5200,"Blaster (USA) (Proto)",16384,0xc8f9c094}, + { a5200,"Blue Print",16384,0x0624e6e7}, + { a5200_40,"Bounty Bob Strikes Back",40960,0x7873c6dd}, + { a5200_ee_16,"Buck Rogers - Planet of Zoom",16384,0x04807705}, + { a5200_ee_16,"Centipede",16384,0x536a70fe}, + { a5200,"Choplifter!",16384,0x9ad53bbc}, + { a5200_ee_16,"Congo Bongo",16384,0xf1f42bbd}, + { a5200_ee_16,"Counter Measure",16384,0xfd541c80}, + { a5200,"Dave Crane's Pitfall II - Lost Caverns",16384,0x4b910461}, + { a5200_ee_16,"Defender",16384,0xbd52623b}, + { a5200_ee_16,"Dig Dug",16384,0x6a687f9c}, + { a5200,"Dreadnaught Factor, The",8192,0x460def2d}, + { a5200,"Final Legacy (Proto)",16384,0xd3bd3221}, { a5200_ee_16,"friskyt.bin",16384,0x04b299a4}, - { a5200,"frogger.bin",8192,0xae7e3444}, - { a5200_ee_16,"frogger2.bin",16384,0x0af19345}, - { a5200,"galaxian.bin",8192,0x3ef4a23f}, + { a5200,"Frisky Tom (Mouse Speed Fix)",32768,0x04b299a4}, + { a5200,"Frogger",8192,0xae7e3444}, + { a5200_ee_16,"Frogger II - Threeedeep!",16384,0x0af19345}, + { a5200,"Galaxian",8192,0x3ef4a23f}, { a5200,"gorf.bin",8192,0xe955db74}, - { a5200,"gremlins.bin",32768,0x063ec2c4}, - { a5200_ee_16,"gyruss.bin",16384,0xcfd4a7f9}, - { a5200,"hero.bin",16384,0x18a73af3}, - { a5200_ee_16,"jamesbnd.bin",16384,0xd9ae4518}, - { a5200_ee_16,"joust.bin",16384,0xbfd30c01}, - { a5200_ee_16,"jrpacman.bin",16384,0x59983c40}, - { a5200_ee_16,"jungleh.bin",16384,0x2c676662}, - { a5200,"krazysht.bin",8192,0xee702214}, - { a5200,"kaboom.bin",4096,0x420f5d0b}, - { a5200_ee_16,"kangaroo.bin",16384,0xecfa624f}, - { a5200,"keystone.bin",8192,0x8fe3bb2c}, - { a5200,"laststar.bin",16384,0x83517703}, - { a5200_ee_16,"loontoon.bin",16384,0x84df4925}, - { a5200,"mario.bin",32768,0x873742f1}, - { a5200,"meebzork.bin",32768,0x9fb13411}, - { a5200,"megamnia.bin",8192,0x240a1e1a}, - { a5200,"meteorit.bin",16384,0xab8e035b}, + { a5200,"Gremlins",32768,0x063ec2c4}, + { a5200_ee_16,"Gyruss",16384,0xcfd4a7f9}, + { a5200,"H.E.R.O",16384,0x18a73af3}, + { a5200_ee_16,"James Bond 007",16384,0xd9ae4518}, + { a5200_ee_16,"Joust",16384,0xbfd30c01}, + { a5200_ee_16,"J.R. Pac-Man (Proto)",16384,0x59983c40}, + { a5200_ee_16,"Jungle Hunt",16384,0x2c676662}, + { a5200,"K-Razy Shoot-Out",8192,0xee702214}, + { a5200,"Kaboom!",4096,0x420f5d0b}, + { a5200_ee_16,"Kangaroo",16384,0xecfa624f}, + { a5200,"Keystone Kapers",8192,0x8fe3bb2c}, + { a5200,"Last Starfighter, The",16384,0x83517703}, + { a5200_ee_16,"Loony Tunes Hotel (Proto)",16384,0x84df4925}, + { a5200,"Mario Bros.",32768,0x873742f1}, + { a5200,"Meebzork",32768,0x9fb13411}, + { a5200,"MegaMania",8192,0x240a1e1a}, + { a5200,"Meteorites",16384,0xab8e035b}, { a5200_ee_16,"microgam.bin",16384,0x931a454a}, - { a5200,"milliped.bin",16384,0x969cfe1a}, - { a5200,"mine2049.bin",16384,0x7df1adfb}, - { a5200_ee_16,"minigolf.bin",16384,0xc597c087}, - { a5200,"missile.bin",8192,0x44d3ff6f}, - { a5200_ee_16,"montezum.bin",16384,0x2a640143}, - { a5200,"mpatrol.bin",16384,0xd0b2f285}, - { a5200,"mntnking.bin",8192,0x0f24243c}, - { a5200,"docastle.bin",8192,0xaa55f9be}, - { a5200_ee_16,"mspacman.bin",16384,0x752f5efd}, - { a5200_ee_16,"pacman.bin",16384,0x8873ef51}, - { a5200,"pengo.bin",32768,0xe4f8ba8c}, - { a5200,"pitfall.bin",8192,0xb2887833}, - { a5200_ee_16,"polepos.bin",16384,0xabc2d1e4}, - { a5200_ee_16,"popeye.bin",16384,0xa18a9a40}, + { a5200,"Microgammon (Proto)",32768,0x960ce5e2}, + { a5200,"Millipede (Proto).bin",16384,0x969cfe1a}, + { a5200,"Miner 2049er Starring Bounty Bob",16384,0x7df1adfb}, + { a5200_ee_16,"Miniature Golf (Proto)",16384,0xc597c087}, + { a5200,"Missile Command",8192,0x44d3ff6f}, + { a5200_ee_16,"Montezuma's Revenge featuring Panama Joe",16384,0x2a640143}, + { a5200,"Moon Patrol",16384,0xd0b2f285}, + { a5200,"Mountain King",8192,0x0f24243c}, + { a5200,"Mr. Do's Castle",8192,0xaa55f9be}, + { a5200_ee_16,"Ms. Pac-Man",16384,0x752f5efd}, + { a5200_ee_16,"Pac-man",16384,0x8873ef51}, + { a5200,"Pengo",32768,0xe4f8ba8c}, + { a5200,"Pitfall.bin",8192,0xb2887833}, + { a5200_ee_16,"Pole Position",16384,0xabc2d1e4}, + { a5200_ee_16,"Popeye",16384,0xa18a9a40}, { a5200,"qbert.bin",8192,0x3fe4a401}, - { a5200_ee_16,"qix.bin",16384,0xaea6d2c2}, - { a5200,"questroo.bin",16384,0xb5f3402b}, - { a5200,"rsbsebll.bin",32768,0x44166592}, + { a5200_ee_16,"QIX",16384,0xaea6d2c2}, + { a5200,"Quest For Quintana Roo",16384,0xb5f3402b}, + { a5200,"Realsports Baseball",32768,0x44166592}, { a5200,"rsbktbll.bin",32768,0xdd217276}, - { a5200,"rsbktbll1.bin",32768,0xc90196fa}, + { a5200,"Realsports Basketball (Proto1)",32768,0xc90196fa}, { a5200_ee_16,"rsbktbll2.bin",16384,0x0f996184}, - { a5200_ee_16,"rsftball.bin",16384,0x4336c2cc}, - { a5200_ee_16,"rssoccer.bin",16384,0xecbd1853}, - { a5200_ee_16,"rstennis.bin",16384,0x10f33c90}, - { a5200,"fractal.bin",32768,0x762c591b}, - { a5200,"riveraid.bin",8192,0x09fc7648}, - { a5200_ee_16,"roadrun.bin",16384,0xa97606ab}, - { a5200,"robotron.bin",16384,0x4252abd9}, - { a5200_ee_16,"spcedngn.bin",16384,0xb68d61e8}, - { a5200,"spaceinv.bin",8192,0xde5c354a}, - { a5200,"spceshut.bin",16384,0x387365dc}, - { a5200,"spitfire.bin",32768,0x3c311303}, - { a5200_ee_16,"sprtgoof.bin",16384,0x73b5b6fb}, - { a5200_ee_16,"starraid.bin",16384,0x7d819a9f}, - { a5200_ee_16,"startrek.bin",16384,0x69f23548}, - { a5200,"starwars.bin",8192,0x0675f0a5}, - { a5200_ee_16,"swa.bin",16384,0x75f566df}, - { a5200_ee_16,"stargate.bin",16384,0x1d1cee27}, - { a5200,"sprbreak.bin",4096,0xa0642110}, - { a5200,"scobra.bin",8192,0x97debcd2}, - { a5200,"spacman.bin",16384,0x0a4ddb1e}, - { a5200,"tempest.bin",16384,0x1187342f}, - { a5200,"trackfld.bin",16384,0x0ba22ece}, - { a5200,"vanguard.bin",32768,0xcaaea0a4}, - { a5200,"wow.bin",16384,0xd6f7ddfd}, - { a5200_ee_16,"xariarna.bin",16384,0xb8faaec3}, - { a5200,"xevious.bin",32768,0x382634dc}, - { a5200,"yllowsub.bin",4096,0xf47bc091}, - { a5200,"zaxxon.bin",32768,0x741746d1}, - { a5200,"zenji.bin",8192,0xda228530}, - { a5200,"znerangr.bin",16384,0x2959d827}, + { a5200_ee_16,"Realsports Football",16384,0x4336c2cc}, + { a5200_ee_16,"Realsports Soccer",16384,0xecbd1853}, + { a5200_ee_16,"RealSports Tennis",16384,0x10f33c90}, + { a5200,"Rescue on Fractalus!",32768,0x762c591b}, + { a5200,"Carol Shaw's River Raid",8192,0x09fc7648}, + { a5200_ee_16,"Road Runner (Proto)",16384,0xa97606ab}, + { a5200,"Robotron 2084",16384,0x4252abd9}, + { a5200_ee_16,"Space Dungeon",16384,0xb68d61e8}, + { a5200,"Space Invaders",8192,0xde5c354a}, + { a5200,"Space Shuttle",16384,0x387365dc}, + { a5200,"Spitfire (Proto)",32768,0x3c311303}, + { a5200_ee_16,"Sport Goofy (Proto)",16384,0x73b5b6fb}, + { a5200_ee_16,"Star Raiders",16384,0x7d819a9f}, + { a5200_ee_16,"Star Trek - Strategic Operations Simulator",16384,0x69f23548}, + { a5200,"Star Wars - Death Star Battle",8192,0x0675f0a5}, + { a5200_ee_16,"Star Wars - The Arcade Game",16384,0x75f566df}, + { a5200_ee_16,"Stargate (Proto)",16384,0x1d1cee27}, + { a5200,"Super Breakout",4096,0xa0642110}, + { a5200,"Super Cobra",8192,0x97debcd2}, + { a5200,"Super Pac-Man (USA) (Proto)",16384,0x0a4ddb1e}, + { a5200,"Tempest (Proto)",16384,0x1187342f}, + { a5200,"Track And Field",16384,0x0ba22ece}, + { a5200,"Vanguard",32768,0xcaaea0a4}, + { a5200,"Wizard Of Wor",16384,0xd6f7ddfd}, + { a5200_ee_16,"Xari Arena (Proto)",16384,0xb8faaec3}, + { a5200_ee_16,"Xari Arena (9-20-83)",16384,0x29178296}, + { a5200,"Xevious",32768,0x382634dc}, + { a5200,"Yellow Submarine (Proto)",4096,0xf47bc091}, + { a5200,"Zaxxon",32768,0x741746d1}, + { a5200,"Zenji",8192,0xda228530}, + { a5200,"Zone Ranger",16384,0x2959d827}, { a5200,"petetest.bin",8192,0x28278cd6}, { a5200_ee_16,"pamdiag2.bin",16384,0xe8b130c4}, { a5200_ee_16,"pamdg23.bin",16384,0xce07d9ad}, { a5200,"finaltst.bin",8192,0x7ea86e87}, { a5200,"boogie.bin",4096,0x3bd5fdd6}, - { a5200,"cblast.bin",32768,0x7c988054}, - { a5200,"ccrisis.bin",32768,0xd50e4061}, - { a5200,"koffiyk.bin",32768,0x917be656}, - { a5200,"tempest (atariage).bin",32768,0xa6400e17}, + { a5200,"Boogie",16384,0x7a9d9f85}, + { a5200,"Castle Blast",32768,0x7c988054}, + { a5200,"Castle Crisis",32768,0xd50e4061}, + { a5200,"Koffi - Yellow Kopter",32768,0x917be656}, + { a5200,"Tempest (Atariage)",32768,0xa6400e17}, + { a5200,"Abracadabra",32768, 0x538a852e}, + { a5200,"3-D Tic-Tac-Toe", 32768, 0x38ec4bfe }, + { a5200,"Adventure 2 (Advanced - Special)", 32768, 0xaa600fac }, + { a5200,"Adventure 2 (Easy - Intermediate)", 32768, 0x69cc2cd6 }, + { a5200,"Alien Assault 2121 (A800)", 32768, 0x0dd8c1a0 }, + { a5200,"Analog Classics #1 (A800)", 32768, 0x3e382e45 }, + { a5200,"Ant Eater (A800)", 16384, 0x821f0d97 }, + { a5200,"Archon (A800)", 32767, 0x7bef7c2d }, + { a5200,"Asteroids (5200 Hack)", 8192, 0x28ec3d93 }, + { a5200,"Asteroids (A800)(Improved Defense-Slow Flip)", 16384, 0x0ce7a6e6 }, + { a5200,"Asteroids (A800)", 16384, 0x4e4bf0bd }, + { a5200,"Asteroids Vector Edition", 16384, 0xdd5ecbaa }, + { a5200,"Atlantis", 32768, 0xc99db95f }, + { a5200,"Attack of the Mutant Camels (A800)", 32768, 0x15cafede }, + { a5200,"Baby Berks", 32768, 0x8bc856cc }, + { a5200,"Bacterion (A800)", 8192, 0x4a2056d5 }, + { a5200,"Barnstorming", 16384, 0xf9addb80 }, + { a5200,"Basketball (A800)", 16384, 0x84acbf42 }, + { a5200,"Batty Builders (A800)", 32768, 0x67261f79 }, + { a5200,"BC's Quest For Tires (A800)", 32768, 0xa3a16596 }, + { a5200,"Beef Drop - Ultimate SD Digital Edition", 32768, 0x55545848 }, + { a5200,"Beef_Drop Demo (Fixed level 3)", 32768, 0x48a43bae }, + { a5200,"Blowsub", 16384, 0x3aa7bc0e }, + { a5200,"Boogie", 16384, 0x7a9d9f85 }, + { a5200,"BoulderDash (A800)", 32768, 0x00b9a3f4 }, + { a5200_40,"Bounty Bob Strikes Back", 40960, 0x57e7945e }, + { a5200,"Bowling", 16384, 0xd7237961 }, + { a5200,"Buried Bucks (A800)", 32768, 0x1d161671 }, + { a5200,"Captain Beeble (A800)", 32768, 0x2b760c6e }, + { a5200,"Capture The Flag (A800)", 16384, 0xd1d31e79 }, + { a5200,"Caverns Of Mars 2 (A800)", 16384, 0x2fdb70c1 }, + { a5200,"Caverns Of Mars (A800)", 32768, 0xd1b64581 }, + { a5200,"Caverns Of The Lost Miner (A800)", 32768, 0xff93b2db }, + { a5200,"Chess (Parker Brothers) (A800)", 32768, 0x0f6c1af4 }, + { a5200,"Chicken (A800)", 32768, 0xb2d018b1 }, + { a5200,"Chop Suey", 32768, 0x0244c402 }, //Atari800 improperly detects this as an ATARI Basic file. + { a5200,"Christmas Cart", 16384, 0x38f4a6a4 }, + { a5200,"Claim Jumper (A800)", 16384, 0xb0553a77 }, + { a5200,"Cloud Burst (A800)", 16384, 0x58a0d074 }, + { a5200,"Clowns & Balloons (A800)", 32768, 0x0dafa507 }, + { a5200,"Crossfire (A800)", 32768, 0xd56be4bc }, + { a5200,"Crystal Castles (Clay) (A800)", 32768, 0xd3b10b5d }, + { a5200,"Crystal Castles (A800)", 32768, 0x596F61D1 }, + { a5200,"Curse Of The Lost Miner (A800)", 32768, 0x8cabcad2 }, + { a5200,"Curse Of The Lost Mines (A800)", 32768, 0xc15a1d9d }, + { a5200,"Deluxe Invaders (A800)", 16384, 0xaf796cfc }, + { a5200,"Demon Attack (A800)", 32768, 0x2174730e }, + { a5200,"Desmonds Dungeon (A800)", 32768, 0x1273a5d1 }, + { a5200,"Diamond Mine (A800)", 32768, 0x7882a1c0 }, + { a5200,"Dig Dug 10-9 (A800)", 32768, 0x89fde0da }, + { a5200,"Donkey Kong Arcade (Hack) (A800)", 32768, 0x8eee62a0 }, + { a5200,"Donkey Kong Jr Arcade (Hack) (A800)", 32768, 0x624cf75c }, + { a5200,"Donkey Kong Jr Enhanced (Hack) (A800)", 32768, 0x383a9157 }, + { a5200,"Donkey Kong Jr (A800)", 32768, 0xb136c3f5 }, + { a5200,"Donkey Kong (A800)", 32768, 0xe902c79e }, + { a5200,"Drelbs (A800)", 16384, 0xbce9e1c2 }, + { a5200,"Ducks Ahoy (A800)", 32768, 0x7a10c551 }, + { a5200,"Dust In The Wind", 32768, 0xf92da5f7 }, + { a5200,"Embargo (A800)", 16384, 0x803808cf }, + { a5200,"EMI Pool (A800)", 16384, 0x5b3ceac8 }, + { a5200,"Encounter (A800)", 32768, 0x6bd0efea }, + { a5200,"Enduro (5200)", 8192, 0x327633db }, + { a5200,"ET Phone Home! (A800)", 32768, 0x5e82a934 }, + { a5200,"Fast Eddie (A800)", 32768, 0xbc0d17bd }, + { a5200,"Fast Food (A800)", 16384, 0x246651b1 }, + { a5200,"Fishing Derby (A800)", 16384, 0xf8583e78 }, + { a5200,"Floyd The Droid (A800)", 8192, 0x89b2d7e5 }, + { a5200,"Forbidden Forest - Slinky (A800)", 32768, 0x219615d9 }, + { a5200,"Fort Apocalypse (A800)", 32768, 0x1b809acc }, + { a5200,"Freecell XE (A800)", 32768, 0x2d2a2807 }, + { a5200,"Freeway (A800)", 16384, 0xb506352f }, + { a5200,"Frisky Tom (Mouse speed fix) (Proto)", 32768, 0x37622411 }, + { a5200,"Frostbite 400 (A800)", 16384, 0x97e75867 }, + { a5200,"Galactic Chase (A800)", 8192, 0x4c5f1847 }, + { a5200,"Galaga (Proto) (A800)", 32768, 0xecda2354 }, + { a5200,"Gateway To Apshai (A800)", 32768, 0x59ec9114 }, + { a5200,"Gauntlet Demo", 32768, 0x3f7f9bd9 }, + { a5200,"Gebelli Compilation (A800)", 32768, 0xc05ff97c }, + { a5200,"Gorf (Digital)", 32768, 0x7f732dc9 }, + { a5200,"Gunpowder Charlie (A800)", 8192, 0x8deefa2f }, + { a5200,"Hyperblast! (A800)", 32768, 0x5ae93e04 }, + { a5200,"Intellidiscs", 32768, 0xe76a0fb6 }, + { a5200,"Ixion (A800)", 32768, 0xa84e68b9 }, + { a5200,"Jawbreaker (A800)", 32768, 0xb6150460 }, + { a5200,"Jet Boot Jack (A800)", 32768, 0xaf210f8e }, + { a5200,"Journey To The Planets (A800)", 32768, 0x31498fe6 }, + { a5200,"Jumpman Jr. (A800)", 32768, 0xfb35d43b }, + { a5200,"Juno First (A800)", 32768, 0x34928a3f }, + { a5200,"K-Star Patrol (A800)", 32768, 0xcb5355b9 }, + { a5200,"Kid Grid (A800)", 8192, 0x2ad24e49 }, + { a5200,"Kooky Diver 2021 (A800)", 32768, 0xe64eb7f1 }, + { a5200,"Kooky Klimber 2021 (A800)", 32768, 0xd0fe471b }, + { a5200,"Kooky's Quest 2021 (A800)", 32768, 0xd1425786 }, + { a5200,"Laser Gates (A800)", 32768, 0x73a3f5f4 }, + { a5200,"Magical Fairy Force (A800)", 32768, 0x2619c4f2 }, + { a5200,"Major Blink", 32768, 0xfe9abd19 }, + { a5200,"Mario Bros Arcade (A800)", 32768, 0x0d9ee13e }, + { a5200,"Missile Command Plus (A800)", 32768, 0xda25c34a }, + { a5200,"Mr. Cool (A800)", 32768, 0xb132a38d }, + { a5200,"Ms Pac-Man Encore (A800)", 32768, 0xdaeae9ad }, + { a5200,"Necromancer (A800)", 32768, 0x5b7303ed }, + { a5200,"O'Riley's Mine (A800)", 32768, 0x646bbe82 }, + { a5200,"Oil's Well (A800)", 32768, 0x0f77eb0d }, + { a5200,"Pac-man Fixed Munch", 32768, 0x21966b5c }, + { a5200,"Pac-man Plus (A800)", 32768, 0xd3363d1c }, + { a5200,"Pacman Arcade Demo V2 (A800)", 32768, 0xb3351c89 }, + { a5200,"Pacific Coast Highway (A800)", 32768, 0x4cc139d0 }, + { a5200,"Pastfinder (A800)", 32768, 0x366c9c2a }, + { a5200,"Phobos (A800)", 16384, 0x2fb8412a }, + { a5200,"Phoenix 2021 (A800)", 32768, 0x4ad9e737 }, + { a5200,"Pinhead", 16384, 0x2af2aa4b }, + { a5200,"Piracy 1621 (A800)", 32768, 0x762ec09c }, + { a5200,"Pitfall! (Classics Fix)", 32768, 0x78cd4061 }, + { a5200,"Pitstop (A800)", 32768, 0xc23d81f5 }, + { a5200,"Pooyan (A800)", 32768, 0x28210510 }, + { a5200,"Popeye Arcade (A800)", 32768, 0x87c94e0b }, + { a5200,"Preppie! II (A800)", 32768, 0x790a8be1 }, + { a5200,"Preppie! (A800)", 32768, 0x47dc1314 }, + { a5200,"Protector II (A800)", 32768, 0x2c1615d0 }, + { a5200,"Q-bert (No Button)", 8192, 0xaaf3d843 }, + { a5200,"Rainbow Walker (A800)", 16384, 0xcfe4aa0c }, + { a5200,"Rally Speedway (A800)", 32768, 0xd9e3fb4d }, + { a5200,"Rampage (A800)", 32768, 0xb3f352f2 }, + { a5200,"Ramses' Revenge 2021 BC (A800)", 32768, 0xc5aae92b }, + { a5200,"Raster Music Tracker", 32768, 0xc790a3a0 }, + { a5200,"Ratcatcher (A800)", 32768, 0x17c7708d }, + { a5200,"Realsports Curling", 32768, 0x0b5b6fbb }, + { a5200,"Rob n Banks (A800)", 32768, 0x9c34cc76 }, + { a5200,"Robot Dungeon 2121 (A800)", 32768, 0x971027bd }, + { a5200,"Rockball (A800)", 32768, 0xb975d952 }, + { a5200,"Rolltris (A800)", 32768, 0x39eb84a0 }, + { a5200,"Runner Bear (A800)", 32768, 0x9a4f1056 }, + { a5200,"Satan's Hollow (A800)", 32768, 0xfdf0f296 }, + { a5200,"Savage Pond (A800)", 16384, 0xc1f458e6 }, + { a5200,"Scramble (A800)", 32768, 0x41e48cff }, + { a5200,"Sea Chase (A800)", 16384, 0xa87c92e0 }, + { a5200,"Sea Dragon (A800)", 16384, 0x20b68254 }, + { a5200,"Shamus Case II (A800)", 32768, 0xa0e29983 }, + { a5200,"Shamus (A800)", 32768, 0x04eb6f41 }, + { a5200,"Sinistar (A800)", 32768, 0xfb1429b4 }, + { a5200,"Slime (A800)", 32768, 0x4b098ebc }, + { a5200,"Space Assailants 2121 (A800)", 32768, 0xb10338f5 }, + { a5200,"SpeedAce (A800)", 32768, 0x4e35e0fc }, + { a5200,"Spy Hunter (A800)", 32768, 0x4eff1e32 }, + { a5200,"Star Rider (A800)", 16384, 0xb9a9a96b }, + { a5200,"Tapper (A800)", 32768, 0xc18d30fa }, + { a5200,"Tempest (AtariAge)", 32768, 0x015e08b0 }, + { a5200,"Tennis (A800)", 16384, 0xd7e15040 }, + { a5200,"Thetris (A800)", 32768, 0x2f02d826 }, + { a5200,"TimeRunner (A800)", 32768, 0x6483d20c }, + { a5200,"TimeSlip (A800)", 32768, 0x97a9e3f7 }, + { a5200,"Train 1 (A800)", 32768, 0x6e32458b }, + { a5200,"Train 2 (A800)", 32768, 0x3f26a60c }, + { a5200,"Train 3 (A800)", 32768, 0x8716e7c8 }, + { a5200,"Turmoil (A800)", 32768, 0xe3ae4170 }, + { a5200,"Up'n Down (A800)", 32768, 0xb08ca999 }, + { a5200,"Yahtzee 2021 (A800)", 32768, 0x63a5dc8f }, + { a5200,"Worm War I (A800)", 32768, 0x271fdab5 }, + { a5200,"Yar's Strike (A800)", 8192, 0xd1c6f325 }, + { a5200,"Zaxxon 32k (A800)", 32768, 0xbddfd255 }, + { a5200_64,"Berks4", 65536, 0x56aca9c3 }, + { a5200_64,"Dropzone (A800)", 65536, 0x42214594 }, + { a5200_64,"ET Phone Home! (A800)", 65536, 0x0fd0ef86 }, + { a5200_64,"Laser Hawk (A800)", 65536, 0x2e2bee02 }, + { a5200_64,"Mr. Do (A800)", 65536, 0x4d66d5ab }, + { a5200_64,"M.U.L.E (A800)", 65536, 0x1f21446e }, + { a5200_64,"Oil's Well (Title) (A800)", 65536, 0x6a1ebc7d }, + { a5200_64,"Rampage 64K (A800)", 65536, 0xdf48d7d4 }, + { a5200_64,"Sea Dragon 64k (A800)", 65536, 0x66b4ea6b }, + { a5200_64,"Super Pac-man 64k (A800)", 65536, 0x93866191 }, + { a5200_512,"Bosconian (A800)", 524288, 0xbbc7d63c }, { -1,"",0,0}, } ; - diff --git a/libretro/atari800_hash.h b/libretro/atari800_hash.h new file mode 100644 index 0000000..1d5dd47 --- /dev/null +++ b/libretro/atari800_hash.h @@ -0,0 +1,262 @@ +#define a800 0 // STD_8, STD_16, XEGS_32 +#define a800_40 1 // Bounty Bob +#define a800_WILL_64 2 // 64 KB Williams cartridge +#define a800_XE_07_64 3 // XEGS 64 KB cartridge (banks 0-7) +#define a800_XE_128 4 // XEGS 128 KB cartridge +//#define a800_XE_256 4 // XEGS 256 KB cartridge. Not sure if any exist. +//#define a800_XE_512 5 // XEGS 512 KB cartridge. Not sure if any exist. +//#define a800_XE_1024 6 // XEGS 1 MB cartridge. Not sure if any exist. +#define a800_MAX_128 5 // Atarimax 128 KB Flash cartridge +#define a800_MAX_1024 6 // Atarimax 1 MB Flash cartridge (old) +typedef struct { + int type; + char name[50]; + int size; + ULONG crc; +} a800_rom; + +a800_rom a800_game[] = { + + { a800,"3-D Tic-Tac-Toe",8192,0x1cf50ebe}, + { a800,"Abracadabra!",16384,0xa35c775d}, + { a800_XE_128,"Ace Of Aces",131072,0xebad3ddb}, + { a800,"Activision Decathlon",16384,0x7cc0118b}, + { a800,"Adventure Creator",16384,0x8746d9da}, + { a800_XE_128,"Airball",131072,0xa0ccb3c1}, + { a800,"Alf In Color Caves",16384,0x79df7f9d}, + { a800,"Alien Ambush",8192,0xad7bc30b}, + { a800,"Alien Garden",8192,0x3e27ed0f}, + { a800,"Alpha Shield",8192,0xa7ade454}, + { a800_MAX_1024,"Alternate Reality- The City (MF) (Proto)",1048576,0x52cbd474}, + { a800_MAX_1024,"Alternate Reality- The Dungeon (MF)v9",1048576,0x9fd8d4d3}, + { a800,"Ant Eater",8192,0xc7290722}, + { a800,"Archon- The Light And The Dark",32768,0x2636829f}, + { a800_MAX_128,"Arkanoid I, II, III (MF)",131072,0x70f7bbe5}, + { a800,"Asteroids",8192,0xf9fff4a4}, + { a800,"Astro Chase (First Star Software)",16384,0x18752991}, + { a800,"Astro Chase",16384,0x11f1c7fa}, + { a800,"Atlantis",8192,0xbe0b390c}, + { a800,"Attack At EP-CYG-4",163834,0xdca02ca0}, + { a800,"Attack Of The Mutant Camels",163834,0xc933d741}, + { a800_XE_07_64,"Ballblazer",65536,0x820e5ce5}, + { a800,"Baseball",16384,0x065c3fd2}, + { a800,"Basketball",8192,0x79934851}, + { a800_XE_07_64,"Battlezone",65536,0x692515f2}, + { a800,"BC's Quest For Tires",16384,0xdddc6e36}, + { a800,"Beamrider",16384,0x2b05b8df}, + { a800_MAX_128,"Beyond Castle Wolfenstein (MF)",131926,0xf454689b}, + { a800,"Blue Max",32768,0x003f41ac}, + { a800,"Blaster",16384,0xce1126a2}, + { a800,"Boulderdash",16384,0xaf778329}, + { a800,"Boulders And Bombs",16384,0xab2ec21c}, + { a800_40,"Bounty Bob Strikes Back!",40960,0x0d00f072}, + { a800,"Bristles",16384,0x4263d64d}, + { a800_XE_07_64,"Bruce Lee (Repro)",65536,0x255dfc59}, + { a800,"Buck Rogers - Planet Of Zoom",16384,0x84dd597c}, + { a800,"Captain Beeble",16384,0xad8400b1}, + { a800,"Carnival Massacre (CS)",16384,0xea764851}, + { a800,"Carnival Massacre",16384,0x1baf0c97}, + { a800,"Castle Crisis",32768,0x47acda14}, + { a800,"Castle Hassle",16384,0x5a9e938a}, + { a800,"Castles And Keys",16384,0xbe14c091}, + { a800,"Caverns Of Mars",16384,0x8b9b2f5e}, + { a800,"Centipede",8192,0x44bb1842}, + { a800,"Chess",8192,0x72860db1}, + { a800,"Chicken",8192,0x1ab4d8d8}, + { a800,"Choplifter! (Broderbund)",16384,0x3ebc05ff}, + { a800_XE_07_64,"Choplifter!",65536,0xd426eccc}, + { a800,"Claim Jumper",16384,0x1a333c4a}, + { a800,"Cloudburts",8192,0x259aa18b}, + { a800_XE_128,"Commando",131072,0x28288df4}, // falsely autodetected as an xex file + { a800,"Computer Chess",8192,0x5ce06e94}, + { a800,"Computer War",16384,0x4922aac6}, + { a800_MAX_1024,"Conan (MF)",1048576,0x25feb642}, + { a800,"Congo Bongo",16384,0x7a588045}, + { a800,"Cosmic Life",16384,0x919eaaa9}, + { a800,"Cosmic Tunnels",16384,0xad56c2bf}, + { a800,"Crossfire",8192,0x4d7e0503}, + { a800,"Crystal Castles",32768,0x998fa803}, + { a800,"Dance Fantasy",8192,0xfaec94e7}, + { a800_XE_07_64,"Dark Chambers",65536,0xfa2f132c}, + { a800_XE_07_64,"David's Midnight Magic",65536,0xb7ca61a2}, + { a800,"Defender",16384,0x782a81e4}, + { a800,"Deluxe Invaders",8192,0x15dc9b31}, + { a800,"Demo Cart",16384,0x9bedcdf3}, + { a800,"Demon Attack",8192,0x91328072}, + { a800_XE_07_64,"Desert Falcon",65536,0xa8f9324d}, + { a800,"Diamond Mine",16384,0xd8f9b867}, + { a800,"Dig Dug (1983)",16384,0xfdbc57ed}, + { a800,"Dig Dug",16384,0x6d68114e}, + { a800,"Donkey Kong Jr.",16384,0xa4eb70ef}, + { a800,"Donkey Kong",16384,0xf2b76a27}, + { a800,"Dreadnaught Factor",8192,0xc26fbb5b}, + { a800,"Droids",8192,0x5bb0c159}, + { a800,"Ducks Ahoy",16384,0x4cdfceb9}, + { a800_WILL_64,"DynaKillers",65536,0xad050724}, + { a800,"Eastern Front (1941)",16384,0xccff4a03}, + { a800,"Enduro",8192,0xa538a1bb}, + { a800_MAX_128,"Eidolon- The (MF)",131072,0x46f363f7}, + { a800,"Embargo",8192,0x07b1560c}, + { a800,"Espial",16384,0x64946757}, + { a800,"E.T. Phone Home",16384,0xbce4ef51}, + { a800,"Fantastic Voyage",8192,0x7bde2593}, + { a800,"Fast Eddie",8192,0x53ac386a}, + { a800,"Fast Food",8192,0x77223249}, + { a800_XE_128,"Fight Night",131072,0x4440d167}, + { a800,"Final Legacy",16384,0x6bd0d8e4}, + { a800,"Final Orbit",8192,0x177007e9}, + { a800,"Firebird",8192,0xe3c0b5f1}, + { a800,"Flapper",16384,0x18803c52}, + { a800_XE_128,"Flight Simulator II",131072,0x10cfc489}, + { a800,"Flip And Flop",16384,0x8ae057be}, + { a800_MAX_1024,"Flob 1.0.3b",1048576,0xff236e36}, + { a800,"Food Fight",32767,0x4236f0ea}, + { a800,"Fort Apocalypse",16384,0xf79b33f0}, + { a800,"Fortune Hutner",8192,0x03255972}, + { a800,"Frogger II - Threeedeep!",16384,0xed35f49c}, + { a800,"Frogger",8192,0x40e9476c}, + { a800,"Galaxian",8192,0xd60027be}, + { a800,"Gateway To Apshai",16384,0x3a0c6eb0}, + { a800_XE_128,"G.A.T.O.",131072,0xab06a3f5}, + { a800_MAX_128,"Gauntlet (MF)",131072,0x48cface0}, + { a800,"Gold Mine",8192,0x8459b11e}, + { a800,"Gorf",8192,0x90d0c7d7}, + { a800,"Gridrunner",8192,0x752dd5fe}, + { a800,"Gyruss",16384,0x1da47d01}, + { a800,"H.E.R.O.",16384,0x6062d3ce}, + { a800,"Halftime Battlin Bands",16384,0x8d14b5d3}, + { a800_XE_07_64,"Hardball",65536,0xbf0c6df2}, + { a800,"Hypnotic Land",16384,0x4fb75909}, + { a800,"Into The Eagles Nest",32768,0xf31321c2}, + { a800,"James Bond 007",16384,0x19b4e3a1}, + { a800,"Jawbreaker II",8192,0xe2a63a2d}, + { a800_MAX_1024,"Jim Slide XL",1048576,0x5f74de4a}, + { a800,"Journey To The Planets",16384,0xdce59b65}, + { a800,"Joust",16384,0xf6ec618c}, + { a800,"Jumbo Jet Pilot",16384,0xf046332b}, + { a800_MAX_128,"Jumpman",131072,0x4b7beb03}, + { a800,"Jumpman Jr.",16384,0x6c79bbad}, + { a800,"Jungle Hunt",16384,0x1847a7d4}, + { a800,"K-Razy Antiks",8192,0x84ab21b0}, + { a800,"K-razy Kritters (K-Byte)",8192,0x50354927}, + { a800,"K-razy Kritters",8192,0xf854a3b4}, + { a800,"K-razy Shoot-Out (CBS)",8192,0x636a01f5}, + { a800,"K-razy Shoot-Out (K-Byte)",8192,0x4300f6ff}, + { a800,"K-Star Patrol",8192,0x44c71fae}, + { a800,"Kangaroo",16384,0x1ef94906}, + { a800_XE_128,"Karateka",131072,0x97646f16}, + { a800,"Keystone Kapers",8192,0x465e1763}, + { a800,"Kickback",8192,0x2480ed0a}, + { a800_MAX_128,"Koronis Rift (MF)",131072,0xd385b89c}, + { a800_XE_128,"Lode Runner",131072,0x7790f474}, + { a800_MAX_1024,"Lords Of Conquest (MF)",1048576,0x3d6c01bb}, + { a800_MAX_1024,"M.U.L.E. (MF)",1048576,0x0b84f03e}, + { a800,"Major League Hockey",8192,0x4ffbc999}, + { a800_XE_07_64,"Mario Bros. XE",65536,0x7ba07c34}, + { a800,"M.A.S.H.",8192,0xfa041093}, + { a800,"Matterhorn",16384,0xdcc308cf}, + { a800,"Megamania",8192,0xb3c5130c}, + { a800_XE_128,"Mean 18 (Proto)",131072,0x0ee74a89}, + { a800_XE_128,"Midi Maze (Proto)",131072,0x193a53f6}, + { a800,"Millipede",16384,0xfb7e45da}, + { a800,"Miner 2049er",16384,0x6b1478bf}, + { a800,"Missile Command",8192,0xd2e36392}, + { a800,"Mogul Maniac",16384,0x0c391600}, + { a800,"Monster Maze",16384,0x37049e57}, + { a800,"Moon Patrol",16384,0xb845edb8}, + { a800,"Mountain King",8192,0x79748c93}, + { a800,"Mr. Cool",8192,0x1345d10c}, + { a800,"Mr. Do's Castle",8192,0xf1b9a24a}, + { a800,"Mr. TNT",8192,0x701dbdea}, + { a800,"Mrs. Pac-Man",16384,0xf91d18cf}, + { a800_MAX_128,"Mysterious Adventures (MF)",131072,0xadd506c5}, + { a800,"Necromancer",16384,0x39250ff2}, + { a800,"Nightstrike",8192,0x61245a75}, + { a800,"Oil's Well",16384,0x030ecad6}, + { a800,"One-On-One - Dr J. Vs Larry Bird",32768,0xab060567}, + { a800_MAX_1024,"onEscape (121422) (MF)",1048576,0x083fb021}, + { a800,"Orc Attack",16384,0xfbfaefcd}, + { a800,"Ozzy's Orchard",16384,0x1554b983}, + { a800,"Pac-Man",8192,0x61cf6167}, + { a800_MAX_128,"Pac-Man Arcade",131072,0x19bc1482}, + { a800,"Pastfinder",16384,0x12694c3f}, + { a800,"Peanut Butter Panic",8192,0xcdeb7759}, + { a800,"Pengo",16384,0xd8a9fe0a}, + { a800,"Picnic Paranoia",16384,0xe386a621}, + { a800,"Pitfall! II - The Lost Caverns",16384,0x1668cf3b}, + { a800,"Pitfall!",8192,0xb58bdf1c}, + { a800,"Pitstop",16384,0xd49ebf91}, + { a800,"Plattermania",8192,0x6cef6f94}, + { a800,"Pole Position",16384,0x581570C4}, + { a800,"Pool 400",8192,0xa6c2130f}, + { a800,"Popeye",16384,0x00fce79a}, + { a800,"Porky's",16384,0x1733d3fc}, + { a800,"Powerstar",16384,0xdc0dca6e}, + { a800,"Princes And The Frog",8192,0x7ce79281}, + { a800_MAX_1024,"Prince of Persia 211206 (MF)",1048576,0x52df819c}, + { a800,"Protector II",16384,0x374f311f}, + { a800,"Q-bert",8192,0xff3f0472}, + { a800,"Qix",8192,0x967b8051}, + { a800,"Rack'em Up",16384,0x5335d935}, + { a800,"Rally Speedway",16384,0x0a0f6ea2}, + { a800,"Realsports Football",16384,0x5e8951f4}, + { a800,"Realsports Tennis",16384,0x9a34cbdc}, + { a800_XE_07_64,"Rescue On Fractalus",65536,0x1ca549ad}, + { a800,"Risk (Proto)",8192,0x688b0a0c}, + { a800,"River Raid",8192,0x6e601d81}, + { a800,"River Rescue",16384,0xc018c8a0}, + { a800,"Robotron 2084",16384,0x528fc44a}, + { a800,"Satan's Hollow",16384,0x0f7c7934}, + { a800_MAX_128,"Scott Adams Adventures (MF)",131072,0x5446bcb1}, + { a800,"Sea Chase",8192,0x99b5a1dd}, + { a800,"Sea Fox",16384,0x932cc9a8}, + { a800,"Serpentine",8192,0x1b555b41}, + { a800,"Shamus",16384,0xbd3f06ee}, + { a800,"Silicon Warrior",16384,0x0736c8ae}, + { a800,"Slime",16384,0x1babcad6}, + { a800,"Soccer",8192,0x784c7060}, + { a800_MAX_1024,"Space Harrier",1048576,0xca98abfc}, + { a800,"Space Invaders",8192,0x6c811a10}, + { a800,"Space Shuttle",16384,0x66832f68}, + { a800,"Spark Bugs",8192,0xf56eced2}, + { a800,"Speedway Blast",8192,0xf895cc47}, + { a800,"Spider City",8192,0x8f8c3841}, + { a800,"Springer",16384,0x81466b55}, + { a800,"Spy Hunter",16384,0x34df8ffc}, + { a800,"Squish Em",8192,0xe01600b8}, + { a800,"Stargate (Proto)",16384,0xf527b721}, + { a800,"Star Raiders II",32768,0x737d4196}, + { a800,"Star Raiders",8192,0x5ec023ba}, + { a800,"Star Trek - Strategic Operations Simulator",16384,0x9df169d9}, + { a800,"Star Wars - Arcade Game - The",16384,0xaea795f7}, + { a800,"Star Wars - Death Star Battle",16384,0xd9f5cac7}, + { a800,"Starion",16384,0x23faf9b3}, + { a800,"Submarine Commander",16384,0x77198b2b}, + { a800_MAX_1024,"Summer Games (MF)",1048576,0x92325dc3}, + { a800_XE_128,"Summer Games",131072,0x95e6932d}, + { a800,"Super Cobra",8192,0x2af38d2f}, + { a800,"Super Pac-Man (Early Proto)",16384,0xb985be78}, + { a800,"Super Pac-Man (Proto)",16384,0xb518dda8}, + { a800,"Super Zaxxon",16384,0x9e64e13b}, + { a800,"Survival Of The Fittest",8192,0x7f48fbc5}, + { a800_XE_07_64,"Tapper (Cart)",65536,0xeff12440}, + { a800_XE_07_64,"Thunderfox",65536,0xb15ccef2}, + { a800,"Topper",16384,0x0286eea6}, + { a800_XE_07_64,"Tower Toppler (Proto)",65536,0x6b5333bd}, + { a800,"Track And Field",16384,0x9e2484c8}, + { a800,"Turmoil",8192,0xfe48aadf}, + { a800,"Typo Attack",16384,0x89da4ff7}, + { a800,"Typo",8192,0x70585854}, + { a800_MAX_1024,"Ultima II - Revenge of the Enchantress",1048576,0xd2a6db16}, + { a800_MAX_1024,"Ultima III (MF)",1048576,0xb132a1da}, + { a800_MAX_1024,"Ultima IV (MF)",1048576,0x62b1571f}, + { a800,"Up'n Down",16384,0x53ea3bf6}, + { a800,"Wizard Of Wor",16384,0x8017e56a}, + { a800_MAX_128,"World Karate Championship (MF)",131072,0x83c87b17}, + { a800,"Worm War I",8291,0x79934b01}, + { a800_XE_128,"Xenophobe",131072,0x68466666}, + { a800,"Zaxxon",16384,0x21579706}, + { a800,"Zenji",16384,0xebc6ec2e}, + { a800,"Zone Ranger",16384,0x8f1e72e7}, + { -1,"",0,0}, +} ; diff --git a/libretro/cmdline.c b/libretro/cmdline.c index c4a97e4..8ec0dbf 100644 --- a/libretro/cmdline.c +++ b/libretro/cmdline.c @@ -22,7 +22,7 @@ void Add_Option(const char* option) first++; } - sprintf(XARGV[PARAMCOUNT++],"%s\0",option); + sprintf(XARGV[PARAMCOUNT++],"%s", option); } int pre_main(const char *argv) diff --git a/libretro/core-mapper.c b/libretro/core-mapper.c index 91c82c3..c0bd282 100644 --- a/libretro/core-mapper.c +++ b/libretro/core-mapper.c @@ -3,22 +3,17 @@ #include "retroscreen.h" #include "platform.h" #include "vkbd.h" +#include "input.h" +#include "pokey.h" + //CORE VAR #ifdef _WIN32 char slash = '\\'; #else char slash = '/'; #endif -extern const char *retro_save_directory; -extern const char *retro_system_directory; -extern const char *retro_content_directory; -char RETRO_DIR[512]; - -char DISKA_NAME[512]="\0"; -char DISKB_NAME[512]="\0"; -char TAPE_NAME[512]="\0"; -extern void Screen_SetFullUpdate(int scr); +//extern void retro_message(const char* text, unsigned int frames, int alt); long frame=0; unsigned long Ktime=0 , LastFPSTime=0; @@ -35,48 +30,64 @@ short signed int SNDBUF[1024*2]; int snd_sampler_pal = 44100 / 50; int snd_sampler_ntsc = 44100 / 60; -//PATH -char RPATH[512]; - //EMU FLAGS -int NPAGE=-1, KCOL=1, BKGCOLOR=0; -int SHOWKEY=-1; -int VKBD_OPACITY=-1; +int NPAGE = -1, KCOL = 1, BKGCOLOR = 0; +int SHOWKEY = -1, SHOWKEYDELAY = 0; +int VKBD_OPACITY = -1; #if defined(ANDROID) || defined(__ANDROID__) -int MOUSE_EMULATED=1; +int MOUSE_EMULATED = 1; #else -int MOUSE_EMULATED=-1; +int MOUSE_EMULATED = -1; #endif + int SHIFTON=-1,MOUSEMODE=-1,PAS=4; int SND=1; //SOUND ON/OFF int pauseg=0; //enter_gui int touch=-1; // gui mouse btn + //JOY int al[2][2];//left analog1 int ar[2][2];//right analog1 unsigned char MXjoy[4]; // joy int16_t joypad_bits[4]; +extern UBYTE consol_mask; #define JOYRANGE_UP_VALUE -16384 /* Joystick ranges in XY */ #define JOYRANGE_DOWN_VALUE 16383 #define JOYRANGE_LEFT_VALUE -16384 #define JOYRANGE_RIGHT_VALUE 16383 -extern int a5200_joyhack; - +// retro core option variables +extern int atari_joyhack; extern int keyboard_type; - -//MOUSE +extern int pot_analog_deadzone; +extern int paddle_mode; +extern int paddle_speed; + +extern int INPUT_joy_5200_center; +extern int INPUT_joy_5200_min; +extern int INPUT_joy_5200_max; +extern int INPUT_digital_5200_min; +extern int INPUT_digital_5200_center; +extern int INPUT_digital_5200_max; + +//MOUSE Is this even tied to anything? extern int pushi; // gui mouse btn int gmx,gmy; //gui mouse int mouse_wu=0,mouse_wd=0; + +//Atari800 EMU mouse +extern UBYTE POKEY_POT_input[8]; +extern int INPUT_mouse_pot_min; +extern int INPUT_mouse_pot_max; + //KEYBOARD -char Key_Sate[512]; -char Key_Sate2[512]; -static char old_Key_Sate[512]; +char Key_State[512]; +static char old_Key_State[512]; -int mbt[16]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; +int mbt[4][16] = { {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 } + , { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 }, { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 } }; //STATS GUI int BOXDEC= 32+2; @@ -84,7 +95,6 @@ int STAT_BASEY; /*static*/ retro_input_state_t input_state_cb; static retro_input_poll_t input_poll_cb; -extern void retro_audio_cb( short l, short r); extern bool libretro_supports_bitmasks; @@ -114,13 +124,14 @@ int slowdown=0; #define RETRO_DEVICE_ATARI_KEYBOARD RETRO_DEVICE_SUBCLASS(RETRO_DEVICE_KEYBOARD, 0) #define RETRO_DEVICE_ATARI_JOYSTICK RETRO_DEVICE_SUBCLASS(RETRO_DEVICE_JOYPAD, 1) +#define RETRO_DEVICE_ATARI_5200_JOYSTICK RETRO_DEVICE_SUBCLASS(RETRO_DEVICE_JOYPAD, 2) void texture_uninit(void) { } void texture_init(void) { memset(Retro_Screen, 0, sizeof(Retro_Screen)); - memset(old_Key_Sate ,0, sizeof(old_Key_Sate)); + memset(old_Key_State ,0, sizeof(old_Key_State)); gmx=(retrow/2)-1; gmy=(retroh/2)-1; @@ -151,7 +162,13 @@ void retro_sound_update(void) } } -extern void vkbd_key(int key,int pressed); +//extern void vkbd_key(int key,int pressed); + +void Screen_SetFullUpdate(int scr) +{ + if (scr == 0 || scr > 1) + memset(Retro_Screen, 0, sizeof(Retro_Screen)); +} void vkbd_key(int key,int pressed) { @@ -159,14 +176,14 @@ void vkbd_key(int key,int pressed) { if(SHIFTON==1) ; - Key_Sate[key]=1; + Key_State[key]=1; // key is being held down } else { if(SHIFTON==1) ; - Key_Sate[key]=0; + Key_State[key]=0; // key is being RELEASE } } @@ -229,7 +246,7 @@ void retro_virtualkb(void) virtual_kdb(( char *)Retro_Screen,vkx,vky); - i=8; + i=0; // swapped Button 1 and 2 definitions ( used to be 8 ) if( (joypad_bits[0] & (1 << i)) && vkflag[4]==0 ) vkflag[4]=1; else if( !(joypad_bits[0] & (1 << i)) && vkflag[4]==1 ) @@ -310,12 +327,6 @@ void retro_virtualkb(void) } } -void Screen_SetFullUpdate(int scr) -{ - if(scr==0 ||scr>1) - memset(Retro_Screen, 0, sizeof(Retro_Screen)); -} - void Process_key(void) { int i; @@ -324,13 +335,13 @@ void Process_key(void) return; for(i=0;i<320;i++) - Key_Sate[i]=input_state_cb(0, RETRO_DEVICE_KEYBOARD, 0,i) ? 0x80: 0; + Key_State[i]=input_state_cb(0, RETRO_DEVICE_KEYBOARD, 0,i) ? 0x80: 0; - if(memcmp( Key_Sate,old_Key_Sate , sizeof(Key_Sate) ) ) + if(memcmp( Key_State,old_Key_State , sizeof(Key_State) ) ) { - for(i=0;i<320;i++) + for(i=0;i<320;i++) { - if(Key_Sate[i] && Key_Sate[i]!=old_Key_Sate[i] ) + if(Key_State[i] && Key_State[i]!=old_Key_State[i] ) { if(i==RETROK_RCTRL) { @@ -352,7 +363,7 @@ void Process_key(void) //retro_key_down(i); } - else if ( !Key_Sate[i] && Key_Sate[i]!=old_Key_Sate[i] ) + else if ( !Key_State[i] && Key_State[i]!=old_Key_State[i] ) { if(i==RETROK_RCTRL) { @@ -378,7 +389,52 @@ void Process_key(void) } } - memcpy(old_Key_Sate,Key_Sate , sizeof(Key_Sate) ); + memcpy(old_Key_State,Key_State , sizeof(Key_State) ); +} + +int Atari_POT(int input) +{ + int which = input / 2; + int xval, yval, pval, result; + + //if (consol_mask == 0x0f) + // return 228; + + /* account for Joystick swap enabled */ + if (atari_joyhack == 2) + which = (which + 1) % 2; + + xval = input_state_cb(which, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_LEFT, RETRO_DEVICE_ID_ANALOG_X); + yval = input_state_cb(which, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_LEFT, RETRO_DEVICE_ID_ANALOG_Y); + + pval = (input & 1) ? yval : xval; + + if ((pval > -pot_analog_deadzone) && (pval < pot_analog_deadzone)) + return INPUT_joy_5200_center; + + /* Convert to amplitude */ + float amplitude = (float)((pval > pot_analog_deadzone) ? + (pval - pot_analog_deadzone) : + (pval + pot_analog_deadzone)) / + (float)(LIBRETRO_ANALOG_RANGE - pot_analog_deadzone); + + /* Map to Atari 5200 values */ + if (amplitude >= 0.0f) + result = (JOY_5200_CENTER + (unsigned int)(((float)(INPUT_joy_5200_max - JOY_5200_CENTER) * amplitude) + 0.5f)); + else + result = (JOY_5200_CENTER - (unsigned int)(((float)(JOY_5200_CENTER - INPUT_joy_5200_min) * -amplitude) + 0.5f)); + + /* for debug purposes */ + //if (input == 1) + //{ + // char msg[256]; + + // //sprintf(msg, "X=%f, Y=%f, Deadzone=%f. Result- %i\n", xval / (float)LIBRETRO_ANALOG_RANGE, yval / (float)LIBRETRO_ANALOG_RANGE, (float)pot_analog_deadzone, result); + // sprintf(msg, "Joy Min=%i, Joy Max=%i. Result- %i\n", INPUT_joy_5200_max, INPUT_joy_5200_min, result); + // retro_message(msg, 60, 0); + //} + + return result; } int Retro_PollEvent() @@ -386,13 +442,12 @@ int Retro_PollEvent() // RETRO B Y SLT STA UP DWN LEFT RGT A X L R L2 R2 L3 R3 // INDEX 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 - int SAVPAS=PAS; int i,j; static int vbt[4][16]={ - {0x0,0x0,0x0,0x0,0x01,0x02,0x04,0x08,0x80,0x40,0x0,0x0,0x0,0x0,0x0,0x0}, - {0x0,0x0,0x0,0x0,0x01,0x02,0x04,0x08,0x80,0x40,0x0,0x0,0x0,0x0,0x0,0x0}, - {0x0,0x0,0x0,0x0,0x01,0x02,0x04,0x08,0x80,0x40,0x0,0x0,0x0,0x0,0x0,0x0}, - {0x0,0x0,0x0,0x0,0x01,0x02,0x04,0x08,0x80,0x40,0x0,0x0,0x0,0x0,0x0,0x0}, + {0x80,0x0,0x0,0x0,0x01,0x02,0x04,0x08,0x10,0x40,0x0,0x0,0x0,0x0,0x0,0x0}, + {0x80,0x0,0x0,0x0,0x01,0x02,0x04,0x08,0x10,0x40,0x0,0x0,0x0,0x0,0x0,0x0}, + {0x80,0x0,0x0,0x0,0x01,0x02,0x04,0x08,0x10,0x40,0x0,0x0,0x0,0x0,0x0,0x0}, + {0x80,0x0,0x0,0x0,0x01,0x02,0x04,0x08,0x10,0x40,0x0,0x0,0x0,0x0,0x0,0x0}, }; input_poll_cb(); @@ -414,12 +469,13 @@ int Retro_PollEvent() int16_t mouse_x,mouse_y; mouse_x=mouse_y=0; - if(SHOWKEY==-1 && pauseg==0)Process_key(); + if (SHOWKEY==-1 && pauseg==0) + Process_key(); //Joy mode for(j=0;j<4;j++) { - for(i=4;i<10;i++) + for(i=0;i<16;i++) { if(joypad_bits[j] & (1 << i)) MXjoy[j] |= vbt[j][i]; // Joy press @@ -428,21 +484,12 @@ int Retro_PollEvent() } } - if(a5200_joyhack) //hack for robotron right analog act as Joy1 + if (atari_joyhack == 1 && !paddle_mode) //hack for robotron right analog act as Joy1 { -#if 0 - int x,y; -#endif - //emulate Joy1 with joy analog right ar[0][0] = (input_state_cb(0, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_RIGHT, RETRO_DEVICE_ID_ANALOG_X)); ar[0][1] = (input_state_cb(0, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_RIGHT, RETRO_DEVICE_ID_ANALOG_Y)); -#if 0 - x=ar[0][0]; - y=ar[0][1]; -#endif - /* Directions */ if (ar[0][1] <= JOYRANGE_UP_VALUE) @@ -455,40 +502,141 @@ int Retro_PollEvent() else if (ar[0][0] >= JOYRANGE_RIGHT_VALUE) MXjoy[1] |= 0x08; } + else if (atari_joyhack == 2 && !paddle_mode) //hack for Joy 1 / 2 swap. + { + //emulate Joy1 with joy analog right + al[0][0] = (input_state_cb(0, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_LEFT, RETRO_DEVICE_ID_ANALOG_X)); + al[0][1] = (input_state_cb(0, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_LEFT, RETRO_DEVICE_ID_ANALOG_Y)); + + if (libretro_supports_bitmasks) + joypad_bits[1] = input_state_cb(0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_MASK); + else + { + joypad_bits[1] = 0; + joypad_bits[1] |= input_state_cb(0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_MASK) ? 1 : 0; + } + + for (i = 0 ; i < 16; i++) + { + if (joypad_bits[1] & (1 << i)) + MXjoy[1] |= vbt[0][i]; // Joy press + else if (MXjoy[1] & vbt[0][i]) + MXjoy[1] &= ~vbt[0][i]; // Joy press + } + + /* Directions */ + + if (al[0][1] <= JOYRANGE_UP_VALUE) + MXjoy[1] |= 0x01; + else if (al[0][1] >= JOYRANGE_DOWN_VALUE) + MXjoy[1] |= 0x02; + + if (al[0][0] <= JOYRANGE_LEFT_VALUE) + MXjoy[1] |= 0x04; + else if (al[0][0] >= JOYRANGE_RIGHT_VALUE) + MXjoy[1] |= 0x08; + } - - if(atari_devices[0]==RETRO_DEVICE_ATARI_JOYSTICK) + if ( atari_devices[0] != RETRO_DEVICE_ATARI_KEYBOARD) { //shortcut for joy mode only //Button B Y SLT STA // 0 1 2 3 - for(i=0;i<4;i++) - { - if ( (joypad_bits[0] & (1 << i)) && mbt[i]==0 ) - mbt[i]=1; - else if (mbt[i]==1 && !(joypad_bits[0] & (1 << i)) ) - { - mbt[i]=0; - if(i==2) - MOUSE_EMULATED = -MOUSE_EMULATED; - } - } - //Button L R L2 R2 L3 R3 - // 10 11 12 13 14 15 - for(i=10;i<16;i++) - { - if ( (joypad_bits[0] & (1 << i)) && mbt[i]==0 ) - mbt[i]=1; - else if ( mbt[i]==1 && !(joypad_bits[0] & (1 << i)) ) - { - mbt[i]=0; - if(i==14) - SHOWKEY = -SHOWKEY; - } - } - }//if atari_devices=joy + for (i = 0; i < 16; i++) + { + for (int j = 0; j < 4; j++) + { + if ((joypad_bits[j] & (1 << i)) && mbt[j][i] == 0) + mbt[j][i] = 1; + else if (mbt[j][i] == 1 && !(joypad_bits[j] & (1 << i))) + { + mbt[j][i] = 0; +#if defined(ANDROID) || defined(__ANDROID__) + /* apparently this is used by the ANDROID port? Keep in for now. Android only*/ + if(i==2) + MOUSE_EMULATED = -MOUSE_EMULATED; +#endif + } + } + } + + /* Huh? */ + //for(i=0;i<4;i++) + //{ + // if ( (joypad_bits[0] & (1 << i)) && mbt[i]==0 ) + // mbt[i]=1; + // else if (mbt[i]==1 && !(joypad_bits[0] & (1 << i)) ) + // { + // mbt[i]=0; + // //if(i==2) /* hard coded? android only? */ + // // MOUSE_EMULATED = -MOUSE_EMULATED; + // } + //} + ////Button L R L2 R2 L3 R3 + //// 10 11 12 13 14 15 + //for(i=10;i<16;i++) + //{ + // if ( (joypad_bits[0] & (1 << i)) && mbt[i]==0 ) + // mbt[i]=1; + // else if ( mbt[i]==1 && !(joypad_bits[0] & (1 << i)) ) + // { + // mbt[i]=0; + // //if(i==14) /* removed can be defined in controller settings screen? */ + // //SHOWKEY = -SHOWKEY; + // } + //} + } + else //Emulate joystick controls with keyboard/retro_keyboard only Joy 1 atm... experimental + { + // these are the same for both computer and 5200 + if (Key_State[RETROK_KP8]) // up + MXjoy[0] |= 0x01; + else if (Key_State[RETROK_KP2]) // down + MXjoy[0] |= 0x02; + if (Key_State[RETROK_KP4]) // left + MXjoy[0] |= 0x04; + else if (Key_State[RETROK_KP6]) // right + MXjoy[0] |= 0x08; + + if (Key_State[RETROK_RALT]) // fire 1 + MXjoy[0] |= 0x80; + + // 5200 fire button 2 + if (Atari800_machine_type == Atari800_MACHINE_5200 && !UI_is_active) + if ( Key_State[RETROK_RCTRL]) + INPUT_key_shift = 1; + } + + if (paddle_mode) + { + for (int i = 0; i < 4; i++) + { + int pval = input_state_cb(i, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_LEFT, RETRO_DEVICE_ID_ANALOG_X); + + if ((pval < -pot_analog_deadzone) || (pval > pot_analog_deadzone)) + { + if (pval > 0) + POKEY_POT_input[i] -= paddle_speed; + else + POKEY_POT_input[i] += paddle_speed; + } + else + { + if (joypad_bits[0] & (1 << RETRO_DEVICE_ID_JOYPAD_RIGHT)) + POKEY_POT_input[i] -= paddle_speed; + if (joypad_bits[0] & (1 << RETRO_DEVICE_ID_JOYPAD_LEFT)) + POKEY_POT_input[i] += paddle_speed; + } + + if (POKEY_POT_input[i] < INPUT_mouse_pot_min) + POKEY_POT_input[i] = INPUT_mouse_pot_min; + + if (POKEY_POT_input[i] > INPUT_mouse_pot_max) + POKEY_POT_input[i] = INPUT_mouse_pot_max; + } + } if(MOUSE_EMULATED==1) { @@ -503,10 +651,8 @@ int Retro_PollEvent() mouse_y += PAS; if (joypad_bits[0] & (1 << RETRO_DEVICE_ID_JOYPAD_UP)) mouse_y -= PAS; - mouse_l = (joypad_bits[0] & (1 << RETRO_DEVICE_ID_JOYPAD_A)) ? 1 : 0; - mouse_r = (joypad_bits[0] & (1 << RETRO_DEVICE_ID_JOYPAD_B)) ? 1 : 0; - - PAS=SAVPAS; + mouse_l = (joypad_bits[0] & (1 << RETRO_DEVICE_ID_JOYPAD_B)) ? 1 : 0; + mouse_r = (joypad_bits[0] & (1 << RETRO_DEVICE_ID_JOYPAD_A)) ? 1 : 0; slowdown=1; } diff --git a/libretro/libretro-common/include/libretro.h b/libretro/libretro-common/include/libretro.h index 5790173..3d6df6f 100644 --- a/libretro/libretro-common/include/libretro.h +++ b/libretro/libretro-common/include/libretro.h @@ -282,6 +282,15 @@ enum retro_language RETRO_LANGUAGE_PERSIAN = 20, RETRO_LANGUAGE_HEBREW = 21, RETRO_LANGUAGE_ASTURIAN = 22, + RETRO_LANGUAGE_FINNISH = 23, + RETRO_LANGUAGE_INDONESIAN = 24, + RETRO_LANGUAGE_SWEDISH = 25, + RETRO_LANGUAGE_UKRAINIAN = 26, + RETRO_LANGUAGE_CZECH = 27, + RETRO_LANGUAGE_CATALAN_VALENCIA = 28, + RETRO_LANGUAGE_CATALAN = 29, + RETRO_LANGUAGE_BRITISH_ENGLISH = 30, + RETRO_LANGUAGE_HUNGARIAN = 31, RETRO_LANGUAGE_LAST, /* Ensure sizeof(enum) == sizeof(int) */ @@ -712,6 +721,9 @@ enum retro_mod * state of rumble motors in controllers. * A strong and weak motor is supported, and they can be * controlled indepedently. + * Should be called from either retro_init() or retro_load_game(). + * Should not be called from retro_set_environment(). + * Returns false if rumble functionality is unavailable. */ #define RETRO_ENVIRONMENT_GET_INPUT_DEVICE_CAPABILITIES 24 /* uint64_t * -- @@ -1127,6 +1139,13 @@ enum retro_mod * retro_core_option_definition structs to RETRO_ENVIRONMENT_SET_CORE_OPTIONS_INTL. * This allows the core to additionally set option sublabel information * and/or provide localisation support. + * + * If version is >= 2, core options may instead be set by passing + * a retro_core_options_v2 struct to RETRO_ENVIRONMENT_SET_CORE_OPTIONS_V2, + * or an array of retro_core_options_v2 structs to + * RETRO_ENVIRONMENT_SET_CORE_OPTIONS_V2_INTL. This allows the core + * to additionally set optional core option category information + * for frontends with core option category support. */ #define RETRO_ENVIRONMENT_SET_CORE_OPTIONS 53 @@ -1168,7 +1187,7 @@ enum retro_mod * default value is NULL, the first entry in the * retro_core_option_definition::values array is treated as the default. * - * The number of possible options should be very limited, + * The number of possible option values should be very limited, * and must be less than RETRO_NUM_CORE_OPTION_VALUES_MAX. * i.e. it should be feasible to cycle through options * without a keyboard. @@ -1201,6 +1220,7 @@ enum retro_mod * This should only be called if RETRO_ENVIRONMENT_GET_CORE_OPTIONS_VERSION * returns an API version of >= 1. * This should be called instead of RETRO_ENVIRONMENT_SET_VARIABLES. + * This should be called instead of RETRO_ENVIRONMENT_SET_CORE_OPTIONS. * This should be called the first time as early as * possible (ideally in retro_set_environment). * Afterwards it may be called again for the core to communicate @@ -1335,6 +1355,445 @@ enum retro_mod * should be considered active. */ +#define RETRO_ENVIRONMENT_SET_AUDIO_BUFFER_STATUS_CALLBACK 62 + /* const struct retro_audio_buffer_status_callback * -- + * Lets the core know the occupancy level of the frontend + * audio buffer. Can be used by a core to attempt frame + * skipping in order to avoid buffer under-runs. + * A core may pass NULL to disable buffer status reporting + * in the frontend. + */ + +#define RETRO_ENVIRONMENT_SET_MINIMUM_AUDIO_LATENCY 63 + /* const unsigned * -- + * Sets minimum frontend audio latency in milliseconds. + * Resultant audio latency may be larger than set value, + * or smaller if a hardware limit is encountered. A frontend + * is expected to honour requests up to 512 ms. + * + * - If value is less than current frontend + * audio latency, callback has no effect + * - If value is zero, default frontend audio + * latency is set + * + * May be used by a core to increase audio latency and + * therefore decrease the probability of buffer under-runs + * (crackling) when performing 'intensive' operations. + * A core utilising RETRO_ENVIRONMENT_SET_AUDIO_BUFFER_STATUS_CALLBACK + * to implement audio-buffer-based frame skipping may achieve + * optimal results by setting the audio latency to a 'high' + * (typically 6x or 8x) integer multiple of the expected + * frame time. + * + * WARNING: This can only be called from within retro_run(). + * Calling this can require a full reinitialization of audio + * drivers in the frontend, so it is important to call it very + * sparingly, and usually only with the users explicit consent. + * An eventual driver reinitialize will happen so that audio + * callbacks happening after this call within the same retro_run() + * call will target the newly initialized driver. + */ + +#define RETRO_ENVIRONMENT_SET_FASTFORWARDING_OVERRIDE 64 + /* const struct retro_fastforwarding_override * -- + * Used by a libretro core to override the current + * fastforwarding mode of the frontend. + * If NULL is passed to this function, the frontend + * will return true if fastforwarding override + * functionality is supported (no change in + * fastforwarding state will occur in this case). + */ + +#define RETRO_ENVIRONMENT_SET_CONTENT_INFO_OVERRIDE 65 + /* const struct retro_system_content_info_override * -- + * Allows an implementation to override 'global' content + * info parameters reported by retro_get_system_info(). + * Overrides also affect subsystem content info parameters + * set via RETRO_ENVIRONMENT_SET_SUBSYSTEM_INFO. + * This function must be called inside retro_set_environment(). + * If callback returns false, content info overrides + * are unsupported by the frontend, and will be ignored. + * If callback returns true, extended game info may be + * retrieved by calling RETRO_ENVIRONMENT_GET_GAME_INFO_EXT + * in retro_load_game() or retro_load_game_special(). + * + * 'data' points to an array of retro_system_content_info_override + * structs terminated by a { NULL, false, false } element. + * If 'data' is NULL, no changes will be made to the frontend; + * a core may therefore pass NULL in order to test whether + * the RETRO_ENVIRONMENT_SET_CONTENT_INFO_OVERRIDE and + * RETRO_ENVIRONMENT_GET_GAME_INFO_EXT callbacks are supported + * by the frontend. + * + * For struct member descriptions, see the definition of + * struct retro_system_content_info_override. + * + * Example: + * + * - struct retro_system_info: + * { + * "My Core", // library_name + * "v1.0", // library_version + * "m3u|md|cue|iso|chd|sms|gg|sg", // valid_extensions + * true, // need_fullpath + * false // block_extract + * } + * + * - Array of struct retro_system_content_info_override: + * { + * { + * "md|sms|gg", // extensions + * false, // need_fullpath + * true // persistent_data + * }, + * { + * "sg", // extensions + * false, // need_fullpath + * false // persistent_data + * }, + * { NULL, false, false } + * } + * + * Result: + * - Files of type m3u, cue, iso, chd will not be + * loaded by the frontend. Frontend will pass a + * valid path to the core, and core will handle + * loading internally + * - Files of type md, sms, gg will be loaded by + * the frontend. A valid memory buffer will be + * passed to the core. This memory buffer will + * remain valid until retro_deinit() returns + * - Files of type sg will be loaded by the frontend. + * A valid memory buffer will be passed to the core. + * This memory buffer will remain valid until + * retro_load_game() (or retro_load_game_special()) + * returns + * + * NOTE: If an extension is listed multiple times in + * an array of retro_system_content_info_override + * structs, only the first instance will be registered + */ + +#define RETRO_ENVIRONMENT_GET_GAME_INFO_EXT 66 + /* const struct retro_game_info_ext ** -- + * Allows an implementation to fetch extended game + * information, providing additional content path + * and memory buffer status details. + * This function may only be called inside + * retro_load_game() or retro_load_game_special(). + * If callback returns false, extended game information + * is unsupported by the frontend. In this case, only + * regular retro_game_info will be available. + * RETRO_ENVIRONMENT_GET_GAME_INFO_EXT is guaranteed + * to return true if RETRO_ENVIRONMENT_SET_CONTENT_INFO_OVERRIDE + * returns true. + * + * 'data' points to an array of retro_game_info_ext structs. + * + * For struct member descriptions, see the definition of + * struct retro_game_info_ext. + * + * - If function is called inside retro_load_game(), + * the retro_game_info_ext array is guaranteed to + * have a size of 1 - i.e. the returned pointer may + * be used to access directly the members of the + * first retro_game_info_ext struct, for example: + * + * struct retro_game_info_ext *game_info_ext; + * if (environ_cb(RETRO_ENVIRONMENT_GET_GAME_INFO_EXT, &game_info_ext)) + * printf("Content Directory: %s\n", game_info_ext->dir); + * + * - If the function is called inside retro_load_game_special(), + * the retro_game_info_ext array is guaranteed to have a + * size equal to the num_info argument passed to + * retro_load_game_special() + */ + +#define RETRO_ENVIRONMENT_SET_CORE_OPTIONS_V2 67 + /* const struct retro_core_options_v2 * -- + * Allows an implementation to signal the environment + * which variables it might want to check for later using + * GET_VARIABLE. + * This allows the frontend to present these variables to + * a user dynamically. + * This should only be called if RETRO_ENVIRONMENT_GET_CORE_OPTIONS_VERSION + * returns an API version of >= 2. + * This should be called instead of RETRO_ENVIRONMENT_SET_VARIABLES. + * This should be called instead of RETRO_ENVIRONMENT_SET_CORE_OPTIONS. + * This should be called the first time as early as + * possible (ideally in retro_set_environment). + * Afterwards it may be called again for the core to communicate + * updated options to the frontend, but the number of core + * options must not change from the number in the initial call. + * If RETRO_ENVIRONMENT_GET_CORE_OPTIONS_VERSION returns an API + * version of >= 2, this callback is guaranteed to succeed + * (i.e. callback return value does not indicate success) + * If callback returns true, frontend has core option category + * support. + * If callback returns false, frontend does not have core option + * category support. + * + * 'data' points to a retro_core_options_v2 struct, containing + * of two pointers: + * - retro_core_options_v2::categories is an array of + * retro_core_option_v2_category structs terminated by a + * { NULL, NULL, NULL } element. If retro_core_options_v2::categories + * is NULL, all core options will have no category and will be shown + * at the top level of the frontend core option interface. If frontend + * does not have core option category support, categories array will + * be ignored. + * - retro_core_options_v2::definitions is an array of + * retro_core_option_v2_definition structs terminated by a + * { NULL, NULL, NULL, NULL, NULL, NULL, {{0}}, NULL } + * element. + * + * >> retro_core_option_v2_category notes: + * + * - retro_core_option_v2_category::key should contain string + * that uniquely identifies the core option category. Valid + * key characters are [a-z, A-Z, 0-9, _, -] + * Namespace collisions with other implementations' category + * keys are permitted. + * - retro_core_option_v2_category::desc should contain a human + * readable description of the category key. + * - retro_core_option_v2_category::info should contain any + * additional human readable information text that a typical + * user may need to understand the nature of the core option + * category. + * + * Example entry: + * { + * "advanced_settings", + * "Advanced", + * "Options affecting low-level emulation performance and accuracy." + * } + * + * >> retro_core_option_v2_definition notes: + * + * - retro_core_option_v2_definition::key should be namespaced to not + * collide with other implementations' keys. e.g. A core called + * 'foo' should use keys named as 'foo_option'. Valid key characters + * are [a-z, A-Z, 0-9, _, -]. + * - retro_core_option_v2_definition::desc should contain a human readable + * description of the key. Will be used when the frontend does not + * have core option category support. Examples: "Aspect Ratio" or + * "Video > Aspect Ratio". + * - retro_core_option_v2_definition::desc_categorized should contain a + * human readable description of the key, which will be used when + * frontend has core option category support. Example: "Aspect Ratio", + * where associated retro_core_option_v2_category::desc is "Video". + * If empty or NULL, the string specified by + * retro_core_option_v2_definition::desc will be used instead. + * retro_core_option_v2_definition::desc_categorized will be ignored + * if retro_core_option_v2_definition::category_key is empty or NULL. + * - retro_core_option_v2_definition::info should contain any additional + * human readable information text that a typical user may need to + * understand the functionality of the option. + * - retro_core_option_v2_definition::info_categorized should contain + * any additional human readable information text that a typical user + * may need to understand the functionality of the option, and will be + * used when frontend has core option category support. This is provided + * to accommodate the case where info text references an option by + * name/desc, and the desc/desc_categorized text for that option differ. + * If empty or NULL, the string specified by + * retro_core_option_v2_definition::info will be used instead. + * retro_core_option_v2_definition::info_categorized will be ignored + * if retro_core_option_v2_definition::category_key is empty or NULL. + * - retro_core_option_v2_definition::category_key should contain a + * category identifier (e.g. "video" or "audio") that will be + * assigned to the core option if frontend has core option category + * support. A categorized option will be shown in a subsection/ + * submenu of the frontend core option interface. If key is empty + * or NULL, or if key does not match one of the + * retro_core_option_v2_category::key values in the associated + * retro_core_option_v2_category array, option will have no category + * and will be shown at the top level of the frontend core option + * interface. + * - retro_core_option_v2_definition::values is an array of + * retro_core_option_value structs terminated by a { NULL, NULL } + * element. + * --> retro_core_option_v2_definition::values[index].value is an + * expected option value. + * --> retro_core_option_v2_definition::values[index].label is a + * human readable label used when displaying the value on screen. + * If NULL, the value itself is used. + * - retro_core_option_v2_definition::default_value is the default + * core option setting. It must match one of the expected option + * values in the retro_core_option_v2_definition::values array. If + * it does not, or the default value is NULL, the first entry in the + * retro_core_option_v2_definition::values array is treated as the + * default. + * + * The number of possible option values should be very limited, + * and must be less than RETRO_NUM_CORE_OPTION_VALUES_MAX. + * i.e. it should be feasible to cycle through options + * without a keyboard. + * + * Example entries: + * + * - Uncategorized: + * + * { + * "foo_option", + * "Speed hack coprocessor X", + * NULL, + * "Provides increased performance at the expense of reduced accuracy.", + * NULL, + * NULL, + * { + * { "false", NULL }, + * { "true", NULL }, + * { "unstable", "Turbo (Unstable)" }, + * { NULL, NULL }, + * }, + * "false" + * } + * + * - Categorized: + * + * { + * "foo_option", + * "Advanced > Speed hack coprocessor X", + * "Speed hack coprocessor X", + * "Setting 'Advanced > Speed hack coprocessor X' to 'true' or 'Turbo' provides increased performance at the expense of reduced accuracy", + * "Setting 'Speed hack coprocessor X' to 'true' or 'Turbo' provides increased performance at the expense of reduced accuracy", + * "advanced_settings", + * { + * { "false", NULL }, + * { "true", NULL }, + * { "unstable", "Turbo (Unstable)" }, + * { NULL, NULL }, + * }, + * "false" + * } + * + * Only strings are operated on. The possible values will + * generally be displayed and stored as-is by the frontend. + */ + +#define RETRO_ENVIRONMENT_SET_CORE_OPTIONS_V2_INTL 68 + /* const struct retro_core_options_v2_intl * -- + * Allows an implementation to signal the environment + * which variables it might want to check for later using + * GET_VARIABLE. + * This allows the frontend to present these variables to + * a user dynamically. + * This should only be called if RETRO_ENVIRONMENT_GET_CORE_OPTIONS_VERSION + * returns an API version of >= 2. + * This should be called instead of RETRO_ENVIRONMENT_SET_VARIABLES. + * This should be called instead of RETRO_ENVIRONMENT_SET_CORE_OPTIONS. + * This should be called instead of RETRO_ENVIRONMENT_SET_CORE_OPTIONS_INTL. + * This should be called instead of RETRO_ENVIRONMENT_SET_CORE_OPTIONS_V2. + * This should be called the first time as early as + * possible (ideally in retro_set_environment). + * Afterwards it may be called again for the core to communicate + * updated options to the frontend, but the number of core + * options must not change from the number in the initial call. + * If RETRO_ENVIRONMENT_GET_CORE_OPTIONS_VERSION returns an API + * version of >= 2, this callback is guaranteed to succeed + * (i.e. callback return value does not indicate success) + * If callback returns true, frontend has core option category + * support. + * If callback returns false, frontend does not have core option + * category support. + * + * This is fundamentally the same as RETRO_ENVIRONMENT_SET_CORE_OPTIONS_V2, + * with the addition of localisation support. The description of the + * RETRO_ENVIRONMENT_SET_CORE_OPTIONS_V2 callback should be consulted + * for further details. + * + * 'data' points to a retro_core_options_v2_intl struct. + * + * - retro_core_options_v2_intl::us is a pointer to a + * retro_core_options_v2 struct defining the US English + * core options implementation. It must point to a valid struct. + * + * - retro_core_options_v2_intl::local is a pointer to a + * retro_core_options_v2 struct defining core options for + * the current frontend language. It may be NULL (in which case + * retro_core_options_v2_intl::us is used by the frontend). Any items + * missing from this struct will be read from + * retro_core_options_v2_intl::us instead. + * + * NOTE: Default core option values are always taken from the + * retro_core_options_v2_intl::us struct. Any default values in + * the retro_core_options_v2_intl::local struct will be ignored. + */ + +#define RETRO_ENVIRONMENT_SET_CORE_OPTIONS_UPDATE_DISPLAY_CALLBACK 69 + /* const struct retro_core_options_update_display_callback * -- + * Allows a frontend to signal that a core must update + * the visibility of any dynamically hidden core options, + * and enables the frontend to detect visibility changes. + * Used by the frontend to update the menu display status + * of core options without requiring a call of retro_run(). + * Must be called in retro_set_environment(). + */ + +#define RETRO_ENVIRONMENT_SET_VARIABLE 70 + /* const struct retro_variable * -- + * Allows an implementation to notify the frontend + * that a core option value has changed. + * + * retro_variable::key and retro_variable::value + * must match strings that have been set previously + * via one of the following: + * + * - RETRO_ENVIRONMENT_SET_VARIABLES + * - RETRO_ENVIRONMENT_SET_CORE_OPTIONS + * - RETRO_ENVIRONMENT_SET_CORE_OPTIONS_INTL + * - RETRO_ENVIRONMENT_SET_CORE_OPTIONS_V2 + * - RETRO_ENVIRONMENT_SET_CORE_OPTIONS_V2_INTL + * + * After changing a core option value via this + * callback, RETRO_ENVIRONMENT_GET_VARIABLE_UPDATE + * will return true. + * + * If data is NULL, no changes will be registered + * and the callback will return true; an + * implementation may therefore pass NULL in order + * to test whether the callback is supported. + */ + +#define RETRO_ENVIRONMENT_GET_THROTTLE_STATE (71 | RETRO_ENVIRONMENT_EXPERIMENTAL) + /* struct retro_throttle_state * -- + * Allows an implementation to get details on the actual rate + * the frontend is attempting to call retro_run(). + */ + +#define RETRO_ENVIRONMENT_GET_SAVESTATE_CONTEXT (72 | RETRO_ENVIRONMENT_EXPERIMENTAL) + /* int * -- + * Tells the core about the context the frontend is asking for savestate. + * (see enum retro_savestate_context) + */ + +#define RETRO_ENVIRONMENT_GET_HW_RENDER_CONTEXT_NEGOTIATION_INTERFACE_SUPPORT (73 | RETRO_ENVIRONMENT_EXPERIMENTAL) + /* struct retro_hw_render_context_negotiation_interface * -- + * Before calling SET_HW_RNEDER_CONTEXT_NEGOTIATION_INTERFACE, a core can query + * which version of the interface is supported. + * + * Frontend looks at interface_type and returns the maximum supported + * context negotiation interface version. + * If the interface_type is not supported or recognized by the frontend, a version of 0 + * must be returned in interface_version and true is returned by frontend. + * + * If this environment call returns true with interface_version greater than 0, + * a core can always use a negotiation interface version larger than what the frontend returns, but only + * earlier versions of the interface will be used by the frontend. + * A frontend must not reject a negotiation interface version that is larger than + * what the frontend supports. Instead, the frontend will use the older entry points that it recognizes. + * If this is incompatible with a particular core's requirements, it can error out early. + * + * Backwards compatibility note: + * This environment call was introduced after Vulkan v1 context negotiation. + * If this environment call is not supported by frontend - i.e. the environment call returns false - + * only Vulkan v1 context negotiation is supported (if Vulkan HW rendering is supported at all). + * If a core uses Vulkan negotiation interface with version > 1, negotiation may fail unexpectedly. + * All future updates to the context negotiation interface implies that frontend must support + * this environment call to query support. + */ + + /* VFS functionality */ /* File paths: @@ -2224,6 +2683,30 @@ struct retro_frame_time_callback retro_usec_t reference; }; +/* Notifies a libretro core of the current occupancy + * level of the frontend audio buffer. + * + * - active: 'true' if audio buffer is currently + * in use. Will be 'false' if audio is + * disabled in the frontend + * + * - occupancy: Given as a value in the range [0,100], + * corresponding to the occupancy percentage + * of the audio buffer + * + * - underrun_likely: 'true' if the frontend expects an + * audio buffer underrun during the + * next frame (indicates that a core + * should attempt frame skipping) + * + * It will be called right before retro_run() every frame. */ +typedef void (RETRO_CALLCONV *retro_audio_buffer_status_callback_t)( + bool active, unsigned occupancy, bool underrun_likely); +struct retro_audio_buffer_status_callback +{ + retro_audio_buffer_status_callback_t callback; +}; + /* Pass this to retro_video_refresh_t if rendering to hardware. * Passing NULL to retro_video_refresh_t is still a frame dupe as normal. * */ @@ -2548,6 +3031,35 @@ enum retro_pixel_format RETRO_PIXEL_FORMAT_UNKNOWN = INT_MAX }; +enum retro_savestate_context +{ + /* Standard savestate written to disk. */ + RETRO_SAVESTATE_CONTEXT_NORMAL = 0, + + /* Savestate where you are guaranteed that the same instance will load the save state. + * You can store internal pointers to code or data. + * It's still a full serialization and deserialization, and could be loaded or saved at any time. + * It won't be written to disk or sent over the network. + */ + RETRO_SAVESTATE_CONTEXT_RUNAHEAD_SAME_INSTANCE = 1, + + /* Savestate where you are guaranteed that the same emulator binary will load that savestate. + * You can skip anything that would slow down saving or loading state but you can not store internal pointers. + * It won't be written to disk or sent over the network. + * Example: "Second Instance" runahead + */ + RETRO_SAVESTATE_CONTEXT_RUNAHEAD_SAME_BINARY = 2, + + /* Savestate used within a rollback netplay feature. + * You should skip anything that would unnecessarily increase bandwidth usage. + * It won't be written to disk but it will be sent over the network. + */ + RETRO_SAVESTATE_CONTEXT_ROLLBACK_NETPLAY = 3, + + /* Ensure sizeof() == sizeof(int). */ + RETRO_SAVESTATE_CONTEXT_UNKNOWN = INT_MAX +}; + struct retro_message { const char *msg; /* Message to be displayed. */ @@ -2714,6 +3226,213 @@ struct retro_system_info bool block_extract; }; +/* Defines overrides which modify frontend handling of + * specific content file types. + * An array of retro_system_content_info_override is + * passed to RETRO_ENVIRONMENT_SET_CONTENT_INFO_OVERRIDE + * NOTE: In the following descriptions, references to + * retro_load_game() may be replaced with + * retro_load_game_special() */ +struct retro_system_content_info_override +{ + /* A list of file extensions for which the override + * should apply, delimited by a 'pipe' character + * (e.g. "md|sms|gg") + * Permitted file extensions are limited to those + * included in retro_system_info::valid_extensions + * and/or retro_subsystem_rom_info::valid_extensions */ + const char *extensions; + + /* Overrides the need_fullpath value set in + * retro_system_info and/or retro_subsystem_rom_info. + * To reiterate: + * + * If need_fullpath is true and retro_load_game() is called: + * - retro_game_info::path is guaranteed to contain a valid + * path to an existent file + * - retro_game_info::data and retro_game_info::size are invalid + * + * If need_fullpath is false and retro_load_game() is called: + * - retro_game_info::path may be NULL + * - retro_game_info::data and retro_game_info::size are guaranteed + * to be valid + * + * In addition: + * + * If need_fullpath is true and retro_load_game() is called: + * - retro_game_info_ext::full_path is guaranteed to contain a valid + * path to an existent file + * - retro_game_info_ext::archive_path may be NULL + * - retro_game_info_ext::archive_file may be NULL + * - retro_game_info_ext::dir is guaranteed to contain a valid path + * to the directory in which the content file exists + * - retro_game_info_ext::name is guaranteed to contain the + * basename of the content file, without extension + * - retro_game_info_ext::ext is guaranteed to contain the + * extension of the content file in lower case format + * - retro_game_info_ext::data and retro_game_info_ext::size + * are invalid + * + * If need_fullpath is false and retro_load_game() is called: + * - If retro_game_info_ext::file_in_archive is false: + * - retro_game_info_ext::full_path is guaranteed to contain + * a valid path to an existent file + * - retro_game_info_ext::archive_path may be NULL + * - retro_game_info_ext::archive_file may be NULL + * - retro_game_info_ext::dir is guaranteed to contain a + * valid path to the directory in which the content file exists + * - retro_game_info_ext::name is guaranteed to contain the + * basename of the content file, without extension + * - retro_game_info_ext::ext is guaranteed to contain the + * extension of the content file in lower case format + * - If retro_game_info_ext::file_in_archive is true: + * - retro_game_info_ext::full_path may be NULL + * - retro_game_info_ext::archive_path is guaranteed to + * contain a valid path to an existent compressed file + * inside which the content file is located + * - retro_game_info_ext::archive_file is guaranteed to + * contain a valid path to an existent content file + * inside the compressed file referred to by + * retro_game_info_ext::archive_path + * e.g. for a compressed file '/path/to/foo.zip' + * containing 'bar.sfc' + * > retro_game_info_ext::archive_path will be '/path/to/foo.zip' + * > retro_game_info_ext::archive_file will be 'bar.sfc' + * - retro_game_info_ext::dir is guaranteed to contain a + * valid path to the directory in which the compressed file + * (containing the content file) exists + * - retro_game_info_ext::name is guaranteed to contain + * EITHER + * 1) the basename of the compressed file (containing + * the content file), without extension + * OR + * 2) the basename of the content file inside the + * compressed file, without extension + * In either case, a core should consider 'name' to + * be the canonical name/ID of the the content file + * - retro_game_info_ext::ext is guaranteed to contain the + * extension of the content file inside the compressed file, + * in lower case format + * - retro_game_info_ext::data and retro_game_info_ext::size are + * guaranteed to be valid */ + bool need_fullpath; + + /* If need_fullpath is false, specifies whether the content + * data buffer available in retro_load_game() is 'persistent' + * + * If persistent_data is false and retro_load_game() is called: + * - retro_game_info::data and retro_game_info::size + * are valid only until retro_load_game() returns + * - retro_game_info_ext::data and retro_game_info_ext::size + * are valid only until retro_load_game() returns + * + * If persistent_data is true and retro_load_game() is called: + * - retro_game_info::data and retro_game_info::size + * are valid until retro_deinit() returns + * - retro_game_info_ext::data and retro_game_info_ext::size + * are valid until retro_deinit() returns */ + bool persistent_data; +}; + +/* Similar to retro_game_info, but provides extended + * information about the source content file and + * game memory buffer status. + * And array of retro_game_info_ext is returned by + * RETRO_ENVIRONMENT_GET_GAME_INFO_EXT + * NOTE: In the following descriptions, references to + * retro_load_game() may be replaced with + * retro_load_game_special() */ +struct retro_game_info_ext +{ + /* - If file_in_archive is false, contains a valid + * path to an existent content file (UTF-8 encoded) + * - If file_in_archive is true, may be NULL */ + const char *full_path; + + /* - If file_in_archive is false, may be NULL + * - If file_in_archive is true, contains a valid path + * to an existent compressed file inside which the + * content file is located (UTF-8 encoded) */ + const char *archive_path; + + /* - If file_in_archive is false, may be NULL + * - If file_in_archive is true, contain a valid path + * to an existent content file inside the compressed + * file referred to by archive_path (UTF-8 encoded) + * e.g. for a compressed file '/path/to/foo.zip' + * containing 'bar.sfc' + * > archive_path will be '/path/to/foo.zip' + * > archive_file will be 'bar.sfc' */ + const char *archive_file; + + /* - If file_in_archive is false, contains a valid path + * to the directory in which the content file exists + * (UTF-8 encoded) + * - If file_in_archive is true, contains a valid path + * to the directory in which the compressed file + * (containing the content file) exists (UTF-8 encoded) */ + const char *dir; + + /* Contains the canonical name/ID of the content file + * (UTF-8 encoded). Intended for use when identifying + * 'complementary' content named after the loaded file - + * i.e. companion data of a different format (a CD image + * required by a ROM), texture packs, internally handled + * save files, etc. + * - If file_in_archive is false, contains the basename + * of the content file, without extension + * - If file_in_archive is true, then string is + * implementation specific. A frontend may choose to + * set a name value of: + * EITHER + * 1) the basename of the compressed file (containing + * the content file), without extension + * OR + * 2) the basename of the content file inside the + * compressed file, without extension + * RetroArch sets the 'name' value according to (1). + * A frontend that supports routine loading of + * content from archives containing multiple unrelated + * content files may set the 'name' value according + * to (2). */ + const char *name; + + /* - If file_in_archive is false, contains the extension + * of the content file in lower case format + * - If file_in_archive is true, contains the extension + * of the content file inside the compressed file, + * in lower case format */ + const char *ext; + + /* String of implementation specific meta-data. */ + const char *meta; + + /* Memory buffer of loaded game content. Will be NULL: + * IF + * - retro_system_info::need_fullpath is true and + * retro_system_content_info_override::need_fullpath + * is unset + * OR + * - retro_system_content_info_override::need_fullpath + * is true */ + const void *data; + + /* Size of game content memory buffer, in bytes */ + size_t size; + + /* True if loaded content file is inside a compressed + * archive */ + bool file_in_archive; + + /* - If data is NULL, value is unset/ignored + * - If data is non-NULL: + * - If persistent_data is false, data and size are + * valid only until retro_load_game() returns + * - If persistent_data is true, data and size are + * are valid until retro_deinit() returns */ + bool persistent_data; +}; + struct retro_game_geometry { unsigned base_width; /* Nominal video width of game. */ @@ -2812,6 +3531,10 @@ struct retro_core_option_definition const char *default_value; }; +#ifdef __PS3__ +#undef local +#endif + struct retro_core_options_intl { /* Pointer to an array of retro_core_option_definition structs @@ -2825,6 +3548,143 @@ struct retro_core_options_intl struct retro_core_option_definition *local; }; +struct retro_core_option_v2_category +{ + /* Variable uniquely identifying the + * option category. Valid key characters + * are [a-z, A-Z, 0-9, _, -] */ + const char *key; + + /* Human-readable category description + * > Used as category menu label when + * frontend has core option category + * support */ + const char *desc; + + /* Human-readable category information + * > Used as category menu sublabel when + * frontend has core option category + * support + * > Optional (may be NULL or an empty + * string) */ + const char *info; +}; + +struct retro_core_option_v2_definition +{ + /* Variable to query in RETRO_ENVIRONMENT_GET_VARIABLE. + * Valid key characters are [a-z, A-Z, 0-9, _, -] */ + const char *key; + + /* Human-readable core option description + * > Used as menu label when frontend does + * not have core option category support + * e.g. "Video > Aspect Ratio" */ + const char *desc; + + /* Human-readable core option description + * > Used as menu label when frontend has + * core option category support + * e.g. "Aspect Ratio", where associated + * retro_core_option_v2_category::desc + * is "Video" + * > If empty or NULL, the string specified by + * desc will be used as the menu label + * > Will be ignored (and may be set to NULL) + * if category_key is empty or NULL */ + const char *desc_categorized; + + /* Human-readable core option information + * > Used as menu sublabel */ + const char *info; + + /* Human-readable core option information + * > Used as menu sublabel when frontend + * has core option category support + * (e.g. may be required when info text + * references an option by name/desc, + * and the desc/desc_categorized text + * for that option differ) + * > If empty or NULL, the string specified by + * info will be used as the menu sublabel + * > Will be ignored (and may be set to NULL) + * if category_key is empty or NULL */ + const char *info_categorized; + + /* Variable specifying category (e.g. "video", + * "audio") that will be assigned to the option + * if frontend has core option category support. + * > Categorized options will be displayed in a + * subsection/submenu of the frontend core + * option interface + * > Specified string must match one of the + * retro_core_option_v2_category::key values + * in the associated retro_core_option_v2_category + * array; If no match is not found, specified + * string will be considered as NULL + * > If specified string is empty or NULL, option will + * have no category and will be shown at the top + * level of the frontend core option interface */ + const char *category_key; + + /* Array of retro_core_option_value structs, terminated by NULL */ + struct retro_core_option_value values[RETRO_NUM_CORE_OPTION_VALUES_MAX]; + + /* Default core option value. Must match one of the values + * in the retro_core_option_value array, otherwise will be + * ignored */ + const char *default_value; +}; + +struct retro_core_options_v2 +{ + /* Array of retro_core_option_v2_category structs, + * terminated by NULL + * > If NULL, all entries in definitions array + * will have no category and will be shown at + * the top level of the frontend core option + * interface + * > Will be ignored if frontend does not have + * core option category support */ + struct retro_core_option_v2_category *categories; + + /* Array of retro_core_option_v2_definition structs, + * terminated by NULL */ + struct retro_core_option_v2_definition *definitions; +}; + +struct retro_core_options_v2_intl +{ + /* Pointer to a retro_core_options_v2 struct + * > US English implementation + * > Must point to a valid struct */ + struct retro_core_options_v2 *us; + + /* Pointer to a retro_core_options_v2 struct + * - Implementation for current frontend language + * - May be NULL */ + struct retro_core_options_v2 *local; +}; + +/* Used by the frontend to monitor changes in core option + * visibility. May be called each time any core option + * value is set via the frontend. + * - On each invocation, the core must update the visibility + * of any dynamically hidden options using the + * RETRO_ENVIRONMENT_SET_CORE_OPTIONS_DISPLAY environment + * callback. + * - On the first invocation, returns 'true' if the visibility + * of any core option has changed since the last call of + * retro_load_game() or retro_load_game_special(). + * - On each subsequent invocation, returns 'true' if the + * visibility of any core option has changed since the last + * time the function was called. */ +typedef bool (RETRO_CALLCONV *retro_core_options_update_display_callback_t)(void); +struct retro_core_options_update_display_callback +{ + retro_core_options_update_display_callback_t callback; +}; + struct retro_game_info { const char *path; /* Path to game, UTF-8 encoded. @@ -2871,6 +3731,84 @@ struct retro_framebuffer Set by frontend in GET_CURRENT_SOFTWARE_FRAMEBUFFER. */ }; +/* Used by a libretro core to override the current + * fastforwarding mode of the frontend */ +struct retro_fastforwarding_override +{ + /* Specifies the runtime speed multiplier that + * will be applied when 'fastforward' is true. + * For example, a value of 5.0 when running 60 FPS + * content will cap the fast-forward rate at 300 FPS. + * Note that the target multiplier may not be achieved + * if the host hardware has insufficient processing + * power. + * Setting a value of 0.0 (or greater than 0.0 but + * less than 1.0) will result in an uncapped + * fast-forward rate (limited only by hardware + * capacity). + * If the value is negative, it will be ignored + * (i.e. the frontend will use a runtime speed + * multiplier of its own choosing) */ + float ratio; + + /* If true, fastforwarding mode will be enabled. + * If false, fastforwarding mode will be disabled. */ + bool fastforward; + + /* If true, and if supported by the frontend, an + * on-screen notification will be displayed while + * 'fastforward' is true. + * If false, and if supported by the frontend, any + * on-screen fast-forward notifications will be + * suppressed */ + bool notification; + + /* If true, the core will have sole control over + * when fastforwarding mode is enabled/disabled; + * the frontend will not be able to change the + * state set by 'fastforward' until either + * 'inhibit_toggle' is set to false, or the core + * is unloaded */ + bool inhibit_toggle; +}; + +/* During normal operation. Rate will be equal to the core's internal FPS. */ +#define RETRO_THROTTLE_NONE 0 + +/* While paused or stepping single frames. Rate will be 0. */ +#define RETRO_THROTTLE_FRAME_STEPPING 1 + +/* During fast forwarding. + * Rate will be 0 if not specifically limited to a maximum speed. */ +#define RETRO_THROTTLE_FAST_FORWARD 2 + +/* During slow motion. Rate will be less than the core's internal FPS. */ +#define RETRO_THROTTLE_SLOW_MOTION 3 + +/* While rewinding recorded save states. Rate can vary depending on the rewind + * speed or be 0 if the frontend is not aiming for a specific rate. */ +#define RETRO_THROTTLE_REWINDING 4 + +/* While vsync is active in the video driver and the target refresh rate is + * lower than the core's internal FPS. Rate is the target refresh rate. */ +#define RETRO_THROTTLE_VSYNC 5 + +/* When the frontend does not throttle in any way. Rate will be 0. + * An example could be if no vsync or audio output is active. */ +#define RETRO_THROTTLE_UNBLOCKED 6 + +struct retro_throttle_state +{ + /* The current throttling mode. Should be one of the values above. */ + unsigned mode; + + /* How many times per second the frontend aims to call retro_run. + * Depending on the mode, it can be 0 if there is no known fixed rate. + * This won't be accurate if the total processing time of the core and + * the frontend is longer than what is available for one frame. */ + float rate; +}; + /* Callbacks */ /* Environment callback. Gives implementations a way of performing diff --git a/libretro/libretro-common/include/streams/memory_stream.h b/libretro/libretro-common/include/streams/memory_stream.h new file mode 100644 index 0000000..2c6f3fc --- /dev/null +++ b/libretro/libretro-common/include/streams/memory_stream.h @@ -0,0 +1,63 @@ +/* Copyright (C) 2010-2020 The RetroArch team + * + * --------------------------------------------------------------------------------------- + * The following license statement only applies to this file (memory_stream.h). + * --------------------------------------------------------------------------------------- + * + * Permission is hereby granted, free of charge, + * to any person obtaining a copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef _LIBRETRO_SDK_FILE_MEMORY_STREAM_H +#define _LIBRETRO_SDK_FILE_MEMORY_STREAM_H + +#include +#include + +#include + +RETRO_BEGIN_DECLS + +typedef struct memstream memstream_t; + +memstream_t *memstream_open(unsigned writing); + +void memstream_close(memstream_t *stream); + +uint64_t memstream_read(memstream_t *stream, void *data, uint64_t bytes); + +uint64_t memstream_write(memstream_t *stream, const void *data, uint64_t bytes); + +int memstream_getc(memstream_t *stream); + +void memstream_putc(memstream_t *stream, int c); + +char *memstream_gets(memstream_t *stream, char *buffer, size_t len); + +uint64_t memstream_pos(memstream_t *stream); + +void memstream_rewind(memstream_t *stream); + +int64_t memstream_seek(memstream_t *stream, int64_t offset, int whence); + +void memstream_set_buffer(uint8_t *buffer, uint64_t size); + +uint64_t memstream_get_last_size(void); + +uint64_t memstream_get_ptr(memstream_t *stream); + +RETRO_END_DECLS + +#endif diff --git a/libretro/libretro-common/streams/memory_stream.c b/libretro/libretro-common/streams/memory_stream.c new file mode 100644 index 0000000..2aa1c80 --- /dev/null +++ b/libretro/libretro-common/streams/memory_stream.c @@ -0,0 +1,191 @@ +/* Copyright (C) 2010-2020 The RetroArch team + * + * --------------------------------------------------------------------------------------- + * The following license statement only applies to this file (memory_stream.c). + * --------------------------------------------------------------------------------------- + * + * Permission is hereby granted, free of charge, + * to any person obtaining a copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include +#include +#include + +#include + +/* TODO/FIXME - static globals */ +static uint8_t* g_buffer = NULL; +static uint64_t g_size = 0; +static uint64_t last_file_size = 0; + +struct memstream +{ + uint64_t size; + uint64_t ptr; + uint64_t max_ptr; + uint8_t *buf; + unsigned writing; +}; + +void memstream_set_buffer(uint8_t *buffer, uint64_t size) +{ + g_buffer = buffer; + g_size = size; +} + +uint64_t memstream_get_last_size(void) +{ + return last_file_size; +} + +memstream_t *memstream_open(unsigned writing) +{ + memstream_t *stream; + if (!g_buffer || !g_size) + return NULL; + + stream = (memstream_t*)malloc(sizeof(*stream)); + + if (!stream) + return NULL; + + stream->buf = g_buffer; + stream->size = g_size; + stream->ptr = 0; + stream->max_ptr = 0; + stream->writing = writing; + + g_buffer = NULL; + g_size = 0; + + return stream; +} + +void memstream_close(memstream_t *stream) +{ + if (!stream) + return; + + last_file_size = stream->writing ? stream->max_ptr : stream->size; + free(stream); +} + +uint64_t memstream_get_ptr(memstream_t *stream) +{ + return stream->ptr; +} + +uint64_t memstream_read(memstream_t *stream, void *data, uint64_t bytes) +{ + uint64_t avail = 0; + + if (!stream) + return 0; + + avail = stream->size - stream->ptr; + if (bytes > avail) + bytes = avail; + + memcpy(data, stream->buf + stream->ptr, (size_t)bytes); + stream->ptr += bytes; + if (stream->ptr > stream->max_ptr) + stream->max_ptr = stream->ptr; + return bytes; +} + +uint64_t memstream_write(memstream_t *stream, + const void *data, uint64_t bytes) +{ + uint64_t avail = 0; + + if (!stream) + return 0; + + avail = stream->size - stream->ptr; + if (bytes > avail) + bytes = avail; + + memcpy(stream->buf + stream->ptr, data, (size_t)bytes); + stream->ptr += bytes; + if (stream->ptr > stream->max_ptr) + stream->max_ptr = stream->ptr; + return bytes; +} + +int64_t memstream_seek(memstream_t *stream, int64_t offset, int whence) +{ + uint64_t ptr; + + switch (whence) + { + case SEEK_SET: + ptr = offset; + break; + case SEEK_CUR: + ptr = stream->ptr + offset; + break; + case SEEK_END: + ptr = (stream->writing ? stream->max_ptr : stream->size) + offset; + break; + default: + return -1; + } + + if (ptr <= stream->size) + { + stream->ptr = ptr; + return 0; + } + + return -1; +} + +void memstream_rewind(memstream_t *stream) +{ + memstream_seek(stream, 0L, SEEK_SET); +} + +uint64_t memstream_pos(memstream_t *stream) +{ + return stream->ptr; +} + +char *memstream_gets(memstream_t *stream, char *buffer, size_t len) +{ + return NULL; +} + +int memstream_getc(memstream_t *stream) +{ + int ret = 0; + if (stream->ptr >= stream->size) + return EOF; + ret = stream->buf[stream->ptr++]; + + if (stream->ptr > stream->max_ptr) + stream->max_ptr = stream->ptr; + + return ret; +} + +void memstream_putc(memstream_t *stream, int c) +{ + if (stream->ptr < stream->size) + stream->buf[stream->ptr++] = c; + + if (stream->ptr > stream->max_ptr) + stream->max_ptr = stream->ptr; +} diff --git a/libretro/libretro-core.c b/libretro/libretro-core.c index d6dedee..8400e7f 100644 --- a/libretro/libretro-core.c +++ b/libretro/libretro-core.c @@ -1,20 +1,30 @@ #include +#include #include "libretro.h" +#include "libretro_core_options.h" #include "libretro-core.h" +#include "retro_strings.h" + +// DISK CONTROL +#include "retro_disk_control.h" +static dc_storage* dc; + +#include "antic.h" #include "atari.h" +#include "afile.h" #include "devices.h" #include "esc.h" #include "memory.h" #include "cassette.h" #include "artifact.h" +#include "statesav.h" cothread_t mainThread; cothread_t emuThread; static void fallback_log(enum retro_log_level level, const char* fmt, ...); - retro_log_printf_t log_cb = fallback_log; int CROP_WIDTH; @@ -23,28 +33,112 @@ int VIRTUAL_WIDTH; int retrow = 400; int retroh = 300; +#define ATARI_JOYPAD_BUTTONS 16 +#define ATARI_NUMBER_JOYSTICKS 4 + #define RETRO_DEVICE_ATARI_KEYBOARD RETRO_DEVICE_SUBCLASS(RETRO_DEVICE_KEYBOARD, 0) #define RETRO_DEVICE_ATARI_JOYSTICK RETRO_DEVICE_SUBCLASS(RETRO_DEVICE_JOYPAD, 1) +#define RETRO_DEVICE_ATARI_5200_JOYSTICK RETRO_DEVICE_SUBCLASS(RETRO_DEVICE_JOYPAD, 2) + +/* Atari 800 Joysticks 1 thru 4 */ +#define RETRO_DESCRIPTOR_BLOCK(_user) \ + { _user, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_UP, "Up" }, \ + { _user, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_DOWN, "Down" }, \ + { _user, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_LEFT, "Left" }, \ + { _user, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_RIGHT, "Right" }, \ + { _user, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_B, "Fire 1" }, \ + { _user, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_A, "Fire 2" }, \ + { _user, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_Y, "Space" }, \ + { _user, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_X, "Return" }, \ + { _user, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_SELECT, "Select" }, \ + { _user, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_START, "Start" }, \ + { _user, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_L, "Option" }, \ + { _user, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_R, "Atari800 Menu" }, \ + { _user, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_L2, "Esc" }, \ + { _user, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_R2, "Help" }, \ + { _user, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_L3, "Virtual Keyboard" }, \ + { _user, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_R3, "R3" } + +static struct retro_input_descriptor inputDescriptors_a800[] = +{ + RETRO_DESCRIPTOR_BLOCK(0), + RETRO_DESCRIPTOR_BLOCK(1), + RETRO_DESCRIPTOR_BLOCK(2), + RETRO_DESCRIPTOR_BLOCK(3), + {0}, +}; +#undef RETRO_DESCRIPTOR_BLOCK + +/* Atari 5200 Joysticks 1 thru 4 */ +#define RETRO_DESCRIPTOR_BLOCK(_user) \ + { _user, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_UP, "Joystick Up (Digital)" }, \ + { _user, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_DOWN, "Joystick Down (Digital)" }, \ + { _user, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_LEFT, "Joystick Left (Digital)" }, \ + { _user, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_RIGHT, "Joystick Right (Digital)" }, \ + { _user, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_B, "Fire 1" }, \ + { _user, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_A, "Fire 2" }, \ + { _user, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_Y, "Numpad #" }, \ + { _user, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_X, "Numpad *" }, \ + { _user, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_SELECT, "Pause" }, \ + { _user, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_START, "Start" }, \ + { _user, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_L, "Numpad 0" }, \ + { _user, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_R, "Numpad 1" }, \ + { _user, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_L2, "Numpad 2" }, \ + { _user, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_R2, "Numpad 3" }, \ + { _user, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_L3, "Numpad 7" }, \ + { _user, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_R3, "Virtual Keyboard" } + +static struct retro_input_descriptor inputDescriptors_a5200[] = +{ + RETRO_DESCRIPTOR_BLOCK(0), + RETRO_DESCRIPTOR_BLOCK(1), + RETRO_DESCRIPTOR_BLOCK(2), + RETRO_DESCRIPTOR_BLOCK(3), + {0}, +}; +#undef RETRO_DESCRIPTOR_BLOCK + +/* Dynamic inputdescripter */ +static struct retro_input_descriptor inputDescriptors_dyna[(ATARI_JOYPAD_BUTTONS * ATARI_NUMBER_JOYSTICKS) + 1]; unsigned atari_devices[4]; +#define A800_SAVE_STATE_SIZE 1300000 + +// libretro-Atari800 core options variables int keyboard_type = 0; -int autorun5200 = 0; -int a5200_joyhack = 0; +int autorunCartridge = 0; +int atari_joyhack = 0; +int paddle_mode = 0; +int paddle_speed = 3; + +extern int INPUT_joy_5200_center; +extern int INPUT_joy_5200_min; +extern int INPUT_joy_5200_max; +extern int INPUT_digital_5200_min; +extern int INPUT_digital_5200_center; +extern int INPUT_digital_5200_max; + +int pot_analog_deadzone = (int)(0.15f * (float)LIBRETRO_ANALOG_RANGE); int RETROJOY = 0, RETROPT0 = 0, RETROSTATUS = 0, RETRODRVTYPE = 0; int retrojoy_init = 0, retro_ui_finalized = 0; int retro_sound_finalized = 0; +int libretro_runloop_active = 0; + +char old_Atari800_machine_type[100]; + float retro_fps = 49.8607597; long long retro_frame_counter; extern int ToggleTV; extern int CURRENT_TV; -extern int SHIFTON, pauseg, SND, snd_sampler_pal; +extern int SHIFTON, pauseg, SND; extern short signed int SNDBUF[1024 * 2]; -extern char RPATH[512]; -extern char RETRO_DIR[512]; + +char RPATH[512]; +char RETRO_DIR[512]; int cap32_statusbar = 0; #include "cmdline.c" @@ -68,7 +162,6 @@ static retro_environment_t environ_cb; bool libretro_supports_bitmasks = false; - static void fallback_log(enum retro_log_level level, const char* fmt, ...) { va_list va; @@ -80,108 +173,387 @@ static void fallback_log(enum retro_log_level level, const char* fmt, ...) va_end(va); } +int HandleExtension(char* path, char* ext) +{ + int len = strlen(path); + + if (len >= 4 && + path[len - 4] == '.' && + path[len - 3] == ext[0] && + path[len - 2] == ext[1] && + path[len - 1] == ext[2]) + { + return 1; + } + + return 0; +} + +//***************************************************************************** +//***************************************************************************** +// Disk control + +extern void SIO_Dismount(int diskno); +extern int SIO_Mount(int diskno, const char* filename, int b_open_readonly); +extern void CART_Remove(void); +extern int CASSETTE_Insert(const char* filename); +extern void CASSETTE_Remove(void); +extern void CASSETTE_Seek(unsigned int position); + +//extern int SIO_RotateDisks(void); + +void retro_message(const char* text, unsigned int frames, int alt) { + struct retro_message msg; + struct retro_message_ext msg_ext; + + char msg_local[256]; + unsigned int message_interface_version; + + snprintf(msg_local, sizeof(msg_local), "Atari800: %s", text); + msg.msg = msg_local; + msg.frames = frames; + + msg_ext.msg = msg_local; + msg_ext.duration = frames; + msg_ext.priority = 3; + msg_ext.level = RETRO_LOG_INFO; + msg_ext.target = RETRO_MESSAGE_TYPE_NOTIFICATION_ALT; + msg_ext.type = -1 ; + + if (environ_cb(RETRO_ENVIRONMENT_GET_MESSAGE_INTERFACE_VERSION, &message_interface_version) && (message_interface_version >= 1)) + { + if (alt) + environ_cb(RETRO_ENVIRONMENT_SET_MESSAGE_EXT, (void*)&msg_ext); + else + environ_cb(RETRO_ENVIRONMENT_SET_MESSAGE_EXT, (void*)&msg); + } + else + environ_cb(RETRO_ENVIRONMENT_SET_MESSAGE, (void*)&msg); +} + +static int get_image_unit() +{ + int unit = dc->unit; + if (dc->index < dc->count) + { + if (dc_get_image_type(dc->files[dc->index]) == DC_IMAGE_TYPE_TAPE) + dc->unit = DC_IMAGE_TYPE_TAPE; + else if (dc_get_image_type(dc->files[dc->index]) == DC_IMAGE_TYPE_FLOPPY) + dc->unit = DC_IMAGE_TYPE_FLOPPY; + else + dc->unit = DC_IMAGE_TYPE_FLOPPY; + } + else + unit = DC_IMAGE_TYPE_FLOPPY; + + return unit; +} + +static void retro_insert_image() +{ + if (dc->unit == DC_IMAGE_TYPE_TAPE) + { + int error = CASSETTE_Insert(dc->files[dc->index]); + if (!error) + { + CASSETTE_Seek(0); + log_cb(RETRO_LOG_INFO,"[retro_insert_image] Tape (%d) inserted: %s\n", dc->index + 1, dc->names[dc->index]); + } + else + { + log_cb(RETRO_LOG_INFO,"[retro_insert_image] Tape Error (%d): %s\n", error, dc->files[dc->index]); + } + } + else if (dc->unit == DC_IMAGE_TYPE_FLOPPY) + { + int error = SIO_Mount(1, dc->files[dc->index], 0); + if (error) + { + log_cb(RETRO_LOG_INFO,"[retro_insert_image] Disk (%d) Error : %s\n", dc->index + 1, dc->files[dc->index]); + return; + } + log_cb(RETRO_LOG_INFO,"[retro_insert_image] Disk (%d) inserted into drive 1 : %s\n", dc->index + 1, dc->files[dc->index]); + } + else + { + log_cb(RETRO_LOG_INFO,"[retro_insert_image] unsupported image-type : %u\n", dc->unit); + } +} + +static bool retro_set_eject_state(bool ejected) +{ + if (dc) + { + int unit = get_image_unit(); + + if (dc->eject_state == ejected) + return true; + + if (ejected && dc->index <= dc->count && dc->files[dc->index] != NULL) + { + if (unit == DC_IMAGE_TYPE_TAPE) + { + CASSETTE_Remove(); + log_cb(RETRO_LOG_INFO,"[retro_set_eject_state] Tape (%d/%d) ejected %s\n", dc->index + 1, dc->count, dc->names[dc->index]); + } + else + { + SIO_Dismount(1); + log_cb(RETRO_LOG_INFO,"[retro_set_eject_state] Disk (%d/%d) ejected: %s\n", dc->index + 1, dc->count, dc->names[dc->index]); + } + } + else if (!ejected && dc->index < dc->count && dc->files[dc->index] != NULL) + { + retro_insert_image(); + } + + dc->eject_state = ejected; + return true; + } + + return false; +} + +/* Gets current eject state. The initial state is 'not ejected'. */ +static bool retro_get_eject_state(void) +{ + if (dc) + return dc->eject_state; + + return true; +} + +static unsigned retro_get_image_index(void) +{ + if (dc) + return dc->index; + + return 0; +} + +/* Sets image index. Can only be called when disk is ejected. + * The implementation supports setting "no disk" by using an + * index >= get_num_images(). + */ +static bool retro_set_image_index(unsigned index) +{ + // Insert image + if (dc) + { + if (index == dc->index) + return true; + + if (dc->replace) + { + dc->replace = false; + index = 0; + } + + if (index < dc->count && dc->files[index]) + { + dc->index = index; + int unit = get_image_unit(); + log_cb(RETRO_LOG_INFO,"[retro_set_image_index] Unit (%d) image (%d/%d) inserted: %s\n", dc->index + 1, unit, dc->count, dc->files[dc->index]); + return true; + } + } + + return false; +} + +static unsigned retro_get_num_images(void) +{ + if (dc) + return dc->count; + + return 0; +} + +/* Adds a new valid index (get_num_images()) to the internal disk list. + * This will increment subsequent return values from get_num_images() by 1. + * This image index cannot be used until a disk image has been set + * with replace_image_index. */ +static bool retro_add_image_index(void) +{ + if (dc) + { + if (dc->count <= DC_MAX_SIZE) + { + dc->files[dc->count] = NULL; + dc->names[dc->count] = NULL; + dc->types[dc->count] = DC_IMAGE_TYPE_NONE; + dc->count++; + return true; + } + } + + return false; +} + +static bool retro_replace_image_index(unsigned index, const struct retro_game_info* info) +{ + if (dc) + { + if (info != NULL) + { + int error = dc_replace_file(dc, index, info->path); + + if ( error == 2) + retro_message("Duplicate Disk selected. Use Index", 6000, 1); + } + else + { + dc_remove_file(dc, index); + } + + return true; + } + + return false; +} + +static bool retro_get_image_path(unsigned index, char* path, size_t len) +{ + if (len < 1) + return false; + + if (dc) + { + if (index < dc->count) + { + if (!string_is_empty(dc->files[index])) + { + strncpy(path, dc->files[index], len); + return true; + } + } + } + + return false; +} + +static bool retro_get_image_label(unsigned index, char* label, size_t len) +{ + if (len < 1) + return false; + + if (dc) + { + if (index < dc->count) + { + if (!string_is_empty(dc->names[index])) + { + strncpy(label, dc->names[index], len); + return true; + } + } + } + + return false; +} + +static struct retro_disk_control_callback disk_interface = { + retro_set_eject_state, + retro_get_eject_state, + retro_get_image_index, + retro_set_image_index, + retro_get_num_images, + retro_replace_image_index, + retro_add_image_index, +}; + +static struct retro_disk_control_ext_callback disk_interface_ext = { + retro_set_eject_state, + retro_get_eject_state, + retro_get_image_index, + retro_set_image_index, + retro_get_num_images, + retro_replace_image_index, + retro_add_image_index, + NULL, /* disk_set_initial_image, not even sure if I want to use this */ + retro_get_image_path, + retro_get_image_label, +}; + void retro_set_environment(retro_environment_t cb) { + bool option_cats_supported; + environ_cb = cb; static const struct retro_controller_description p1_controllers[] = { { "ATARI Joystick", RETRO_DEVICE_ATARI_JOYSTICK }, + { "ATARI 5200 Joystick", RETRO_DEVICE_ATARI_5200_JOYSTICK }, { "ATARI Keyboard", RETRO_DEVICE_ATARI_KEYBOARD }, }; static const struct retro_controller_description p2_controllers[] = { { "ATARI Joystick", RETRO_DEVICE_ATARI_JOYSTICK }, + { "ATARI 5200 Joystick", RETRO_DEVICE_ATARI_5200_JOYSTICK }, { "ATARI Keyboard", RETRO_DEVICE_ATARI_KEYBOARD }, }; static const struct retro_controller_description p3_controllers[] = { { "ATARI Joystick", RETRO_DEVICE_ATARI_JOYSTICK }, + { "ATARI 5200 Joystick", RETRO_DEVICE_ATARI_5200_JOYSTICK }, { "ATARI Keyboard", RETRO_DEVICE_ATARI_KEYBOARD }, }; static const struct retro_controller_description p4_controllers[] = { { "ATARI Joystick", RETRO_DEVICE_ATARI_JOYSTICK }, + { "ATARI 5200 Joystick", RETRO_DEVICE_ATARI_5200_JOYSTICK }, { "ATARI Keyboard", RETRO_DEVICE_ATARI_KEYBOARD }, }; static const struct retro_controller_info ports[] = { - { p1_controllers, 2 }, // port 1 - { p2_controllers, 2 }, // port 2 - { p3_controllers, 2 }, // port 3 - { p4_controllers, 2 }, // port 4 + { p1_controllers, 3 }, // port 1 + { p2_controllers, 3 }, // port 2 + { p3_controllers, 3 }, // port 3 + { p4_controllers, 3 }, // port 4 { NULL, 0 } }; - cb(RETRO_ENVIRONMENT_SET_CONTROLLER_INFO, (void*)ports); + /* Initialise core options */ + libretro_set_core_options(environ_cb, &option_cats_supported); - struct retro_variable variables[] = { - { - "atari800_system", - "Atari System; 400/800 (OS B)|800XL (64K)|130XE (128K)|Modern XL/XE(320K CS)|Modern XL/XE(576K)|Modern XL/XE(1088K)|5200", - }, - { - "atari800_ntscpal", - "Video Standard; NTSC|PAL", - }, - { - "atari800_internalbasic", - "Internal BASIC (hold OPTION on boot); disabled|enabled", - }, - { - "atari800_sioaccel", - "SIO Acceleration; disabled|enabled", - }, - { - "atari800_cassboot", - "Boot from Cassette; disabled|enabled", - }, - { - "atari800_artifacting", - "Hi-Res Artifacting; disabled|enabled", - }, - { - "atari800_opt1", - "Autodetect A5200 CartType; disabled|enabled" , - }, - { - "atari800_opt2", - "Joy hack A5200 for robotron; disabled|enabled" , - }, - { - "atari800_resolution", - "Internal resolution; 336x240|320x240|384x240|384x272|384x288|400x300", - }, - { - "atari800_keyboard", - "Retroarch Keyboard type; poll|callback", - }, - - { NULL, NULL }, - }; - - cb(RETRO_ENVIRONMENT_SET_VARIABLES, variables); + cb(RETRO_ENVIRONMENT_SET_CONTROLLER_INFO, (void*)ports); } static void update_variables(void) { - struct retro_variable var; + /* Atari Cartridge Autodetect leave at TOP!!!!!*/ var.key = "atari800_opt1"; var.value = NULL; - if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value) + /* Moved here for better consistency and when system options that require EMU reset to occur */ + if (HandleExtension((char*)RPATH, "a52") || HandleExtension((char*)RPATH, "A52")) + autorunCartridge = 1; + else if (HandleExtension((char*)RPATH, "bin") || HandleExtension((char*)RPATH, "BIN") + || HandleExtension((char*)RPATH, "rom") || HandleExtension((char*)RPATH, "ROM")) + autorunCartridge = 2; + else if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value) { if (strcmp(var.value, "enabled") == 0) - autorun5200 = 1; + autorunCartridge = 1; + else + autorunCartridge = 0; } + /* Controller Hack for Dual Stick or Swap Ports*/ var.key = "atari800_opt2"; var.value = NULL; if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value) { - if (strcmp(var.value, "enabled") == 0) - a5200_joyhack = 1; + if ( (strcmp(var.value, "Dual Stick") == 0) || + (strcmp(var.value, "enabled") == 0)) // to accomodate for old value + atari_joyhack = 1; + else if ( strcmp(var.value,"Swap Ports") == 0) + atari_joyhack = 2; + else + atari_joyhack = 0; } + /* Sets the Atari 800 internal emulated resolution */ var.key = "atari800_resolution"; var.value = NULL; @@ -207,12 +579,25 @@ static void update_variables(void) //reset_screen(); } + /* Set the Atari system emulated */ var.key = "atari800_system"; var.value = NULL; if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value) { - if (strcmp(var.value, "400/800 (OS B)") == 0) + /* I moved "5200" comparison up to top because I'm lazy. :P Leave it here! */ + if (autorunCartridge ==1 || (strcmp(var.value, "5200") == 0)) + { + Atari800_machine_type = Atari800_MACHINE_5200; + MEMORY_ram_size = 16; + Atari800_builtin_basic = FALSE; + Atari800_keyboard_leds = FALSE; + Atari800_f_keys = FALSE; + Atari800_jumper = FALSE; + Atari800_builtin_game = FALSE; + Atari800_keyboard_detached = FALSE; + } + else if (strcmp(var.value, "400/800 (OS B)") == 0) { Atari800_machine_type = Atari800_MACHINE_800; MEMORY_ram_size = 48; @@ -222,7 +607,6 @@ static void update_variables(void) Atari800_jumper = FALSE; Atari800_builtin_game = FALSE; Atari800_keyboard_detached = FALSE; - Atari800_InitialiseMachine(); } else if (strcmp(var.value, "800XL (64K)") == 0) { @@ -234,7 +618,6 @@ static void update_variables(void) Atari800_jumper = FALSE; Atari800_builtin_game = FALSE; Atari800_keyboard_detached = FALSE; - Atari800_InitialiseMachine(); } else if (strcmp(var.value, "130XE (128K)") == 0) { @@ -246,7 +629,6 @@ static void update_variables(void) Atari800_jumper = FALSE; Atari800_builtin_game = FALSE; Atari800_keyboard_detached = FALSE; - Atari800_InitialiseMachine(); } else if (strcmp(var.value, "Modern XL/XE(320K CS)") == 0) { @@ -258,7 +640,6 @@ static void update_variables(void) Atari800_jumper = FALSE; Atari800_builtin_game = FALSE; Atari800_keyboard_detached = FALSE; - Atari800_InitialiseMachine(); } else if (strcmp(var.value, "Modern XL/XE(576K)") == 0) { @@ -270,7 +651,6 @@ static void update_variables(void) Atari800_jumper = FALSE; Atari800_builtin_game = FALSE; Atari800_keyboard_detached = FALSE; - Atari800_InitialiseMachine(); } else if (strcmp(var.value, "Modern XL/XE(1088K)") == 0) { @@ -282,22 +662,16 @@ static void update_variables(void) Atari800_jumper = FALSE; Atari800_builtin_game = FALSE; Atari800_keyboard_detached = FALSE; - Atari800_InitialiseMachine(); } - else if (strcmp(var.value, "5200") == 0) + + if (!libretro_runloop_active || (strcmp(var.value, old_Atari800_machine_type) != 0 )) { - Atari800_machine_type = Atari800_MACHINE_5200; - MEMORY_ram_size = 16; - Atari800_builtin_basic = FALSE; - Atari800_keyboard_leds = FALSE; - Atari800_f_keys = FALSE; - Atari800_jumper = FALSE; - Atari800_builtin_game = FALSE; - Atari800_keyboard_detached = FALSE; Atari800_InitialiseMachine(); + strcpy(old_Atari800_machine_type, var.value); } } + /* Are we running in NTSC (60hz) or Pal Mode (50hz) */ var.key = "atari800_ntscpal"; var.value = NULL; @@ -313,6 +687,7 @@ static void update_variables(void) } } + /* Activate or deactivate built in internal BASIC*/ var.key = "atari800_internalbasic"; var.value = NULL; @@ -321,15 +696,17 @@ static void update_variables(void) if (strcmp(var.value, "enabled") == 0) { Atari800_disable_basic = FALSE; - Atari800_InitialiseMachine(); } else if (strcmp(var.value, "disabled") == 0) { Atari800_disable_basic = TRUE; - Atari800_InitialiseMachine(); } + + if (!libretro_runloop_active) + Atari800_InitialiseMachine(); } + /* Set whether SIO acceleration is activated. Currently an ALL or nothing setup. */ var.key = "atari800_sioaccel"; var.value = NULL; @@ -338,15 +715,19 @@ static void update_variables(void) if (strcmp(var.value, "enabled") == 0) { ESC_enable_sio_patch = Devices_enable_h_patch = Devices_enable_p_patch = Devices_enable_r_patch = TRUE; - Atari800_InitialiseMachine(); } else if (strcmp(var.value, "disabled") == 0) { ESC_enable_sio_patch = Devices_enable_h_patch = Devices_enable_p_patch = Devices_enable_r_patch = FALSE; - Atari800_InitialiseMachine(); } + + if (!libretro_runloop_active) + Atari800_InitialiseMachine(); + else + ESC_UpdatePatches(); /* We need to do this for it to take while the emulator is running */ } + /* Set whether a cassette image will autoboot ( Hold start on boot ). */ var.key = "atari800_cassboot"; var.value = NULL; @@ -355,21 +736,47 @@ static void update_variables(void) if (strcmp(var.value, "enabled") == 0) { CASSETTE_hold_start = 1; - Atari800_InitialiseMachine(); } else if (strcmp(var.value, "disabled") == 0) { CASSETTE_hold_start = 0; - Atari800_InitialiseMachine(); } + + if (!libretro_runloop_active) + Atari800_InitialiseMachine(); } - var.key = "atari800_artifacting"; + /* Set artifacting type. */ + var.key = "atari800_artifacting_mode"; var.value = NULL; - if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value) + if ( environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value) { - if (strcmp(var.value, "enabled") == 0) + int old_artif_mode = ANTIC_artif_mode; + + if (strcmp(var.value, "none") == 0) + { + ANTIC_artif_mode = 0; + } + if (strcmp(var.value, "blue/brown 1") == 0) + { + ANTIC_artif_mode = 1; + } + else if (strcmp(var.value, "blue/brown 2") == 0) + { + ANTIC_artif_mode = 2; + } + else if (strcmp(var.value, "GTIA") == 0) + { + ANTIC_artif_mode = 3; + } + else if (strcmp(var.value, "CTIA") == 0) + { + ANTIC_artif_mode = 4; + } + + /* only do this if changed from off to on */ + if (ANTIC_artif_mode && !old_artif_mode) { if (Atari800_tv_mode == Atari800_TV_NTSC) { @@ -382,7 +789,8 @@ static void update_variables(void) ARTIFACT_SetTVMode(Atari800_TV_PAL); } } - else if (strcmp(var.value, "disabled") == 0) + /* only do this if changed from on to off*/ + else if (!ANTIC_artif_mode && old_artif_mode) { if (Atari800_tv_mode == Atari800_TV_NTSC) { @@ -395,6 +803,29 @@ static void update_variables(void) ARTIFACT_SetTVMode(Atari800_TV_PAL); } } + + ANTIC_UpdateArtifacting(); + } + + /* Set whether paddle mode is active. */ + var.key = "paddle_active"; + var.value = NULL; + + if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value) + { + if (strcmp(var.value, "enabled") == 0) + paddle_mode = 1; + else + paddle_mode = 0; + } + + /* Set paddle movement speed. */ + var.key = "paddle_movement_speed"; + var.value = NULL; + + if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value) + { + paddle_speed = (int)string_to_unsigned(var.value); } var.key = "atari800_keyboard"; @@ -411,6 +842,63 @@ static void update_variables(void) keyboard_type = 1; } } + + /* Digital Joystick/Paddle Sensitivity */ + var.key = "pot_digital_sensitivity"; + var.value = NULL; + INPUT_digital_5200_min = JOY_5200_MIN; + INPUT_digital_5200_max = JOY_5200_MAX; + + if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value ) + { + float range; + + if (string_is_equal(var.value, "100")) + range = 1; + else + range = (float)string_to_unsigned(var.value) / 100.0f; + + INPUT_digital_5200_min = JOY_5200_CENTER - + (unsigned int)(((float)(JOY_5200_CENTER - JOY_5200_MIN) * + range) + 0.5f); + INPUT_digital_5200_max = JOY_5200_CENTER + + (unsigned int)(((float)(JOY_5200_MAX - JOY_5200_CENTER) * + range) + 0.5f); + } + + /* Analog Joystick Sensitivity */ + var.key = "pot_analog_sensitivity"; + var.value = NULL; + INPUT_joy_5200_min = JOY_5200_MIN; + INPUT_joy_5200_max = JOY_5200_MAX; + + if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value ) + { + float range; + + if (string_is_equal(var.value, "100")) + range = 1; + else + range = (float)string_to_unsigned(var.value) / 100.0f; + + INPUT_joy_5200_min = JOY_5200_CENTER - + (unsigned int)(((float)(JOY_5200_CENTER - JOY_5200_MIN) * + range) + 0.5f); + INPUT_joy_5200_max = JOY_5200_CENTER + + (unsigned int)(((float)(JOY_5200_MAX - JOY_5200_CENTER) * + range) + 0.5f); + } + + /* Analog Joystick Deadzone */ + var.key = "pot_analog_deadzone"; + var.value = NULL; + pot_analog_deadzone = (int)(0.15f * (float)LIBRETRO_ANALOG_RANGE); + + if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value ) + { + unsigned deadzone = string_to_unsigned(var.value); + pot_analog_deadzone = (int)((float)deadzone * 0.01f * (float)LIBRETRO_ANALOG_RANGE); + } } static void retro_wrap_emulator() @@ -444,8 +932,7 @@ void Emu_init() { // update_variables(); - memset(Key_Sate, 0, 512); - memset(Key_Sate2, 0, 512); + memset(Key_State, 0, 512); if (!emuThread && !mainThread) { @@ -453,6 +940,8 @@ void Emu_init() { emuThread = co_create(65536 * sizeof(void*), retro_wrap_emulator); } + old_Atari800_machine_type[0] = 0; + update_variables(); } @@ -471,6 +960,13 @@ void retro_shutdown_core(void) void retro_reset(void) { + if (dc) + { + retro_set_eject_state(TRUE); + retro_set_image_index(0); + } + + AFILE_OpenFile(RPATH, 1, 1, 0); } void retro_get_system_av_info(struct retro_system_av_info* info) @@ -504,7 +1000,11 @@ void retro_get_system_av_info(struct retro_system_av_info* info) void retro_init(void) { + unsigned dci_version = 0; struct retro_log_callback log; + dc = dc_create(); + + libretro_runloop_active = 0; if (environ_cb(RETRO_ENVIRONMENT_GET_LOG_INTERFACE, &log)) log_cb = log.log; @@ -538,10 +1038,12 @@ void retro_init(void) retro_save_directory = retro_system_directory; } - if (retro_system_directory == NULL)sprintf(RETRO_DIR, "%s\0", "."); - else sprintf(RETRO_DIR, "%s\0", retro_system_directory); + if (retro_system_directory == NULL) + sprintf(RETRO_DIR, "%s", "." ); + else + sprintf(RETRO_DIR, "%s%c", retro_system_directory, '0'); - sprintf(retro_system_data_directory, "%s/data\0", RETRO_DIR); + sprintf(retro_system_data_directory, "%s/data", RETRO_DIR); log_cb(RETRO_LOG_INFO, "Retro SYSTEM_DIRECTORY %s\n", retro_system_directory); log_cb(RETRO_LOG_INFO, "Retro SAVE_DIRECTORY %s\n", retro_save_directory); @@ -560,29 +1062,22 @@ void retro_init(void) exit(0); } - struct retro_input_descriptor inputDescriptors[] = { - { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_A, "Fire 1" }, - { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_B, "Return" }, - { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_X, "Fire 2" }, - { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_Y, "Help" }, - { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_SELECT, "Select" }, - { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_START, "Start" }, - { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_RIGHT, "Right" }, - { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_LEFT, "Left" }, - { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_UP, "Up" }, - { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_DOWN, "Down" }, - { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_R, "Atari800 Menu" }, - { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_L, "Option" }, - { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_R2, "Esc" }, - { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_L2, "Space" }, - { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_R3, "R3" }, - { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_L3, "Virtual keyboard" }, - { 0 } - }; - environ_cb(RETRO_ENVIRONMENT_SET_INPUT_DESCRIPTORS, &inputDescriptors); + /* set these up early retro_set_controller_port_device() will adjust later */ + for (int i = 0; i < 4; i++) + { + memcpy(inputDescriptors_dyna + ATARI_JOYPAD_BUTTONS * i, inputDescriptors_a800 + ATARI_JOYPAD_BUTTONS * i, (ATARI_JOYPAD_BUTTONS + 1) * sizeof(struct retro_input_descriptor)); + atari_devices[i] = RETRO_DEVICE_ATARI_JOYSTICK; + } - Emu_init(); + environ_cb(RETRO_ENVIRONMENT_SET_INPUT_DESCRIPTORS, &inputDescriptors_dyna); + // Disk control interface + if (environ_cb(RETRO_ENVIRONMENT_GET_DISK_CONTROL_INTERFACE_VERSION, &dci_version) && (dci_version >= 1)) + environ_cb(RETRO_ENVIRONMENT_SET_DISK_CONTROL_EXT_INTERFACE, &disk_interface_ext); + else + environ_cb(RETRO_ENVIRONMENT_SET_DISK_CONTROL_INTERFACE, &disk_interface); + + Emu_init(); texture_init(); if (environ_cb(RETRO_ENVIRONMENT_GET_INPUT_BITMASKS, NULL)) @@ -594,7 +1089,6 @@ void retro_deinit(void) { Emu_uninit(); - co_switch(emuThread); log_cb(RETRO_LOG_INFO, "exit emu\n"); // main_exit(); @@ -606,6 +1100,13 @@ void retro_deinit(void) emuThread = 0; } + // Clean the m3u storage + if (dc) + { + dc_free(dc); + dc = 0; + } + log_cb(RETRO_LOG_INFO, "Retro DeInit\n"); libretro_supports_bitmasks = false; @@ -624,6 +1125,13 @@ void retro_set_controller_port_device(unsigned port, unsigned device) atari_devices[port] = device; printf(" port(%d)=%d \n", port, device); + + if (device == RETRO_DEVICE_ATARI_JOYSTICK) + memcpy(inputDescriptors_dyna + ATARI_JOYPAD_BUTTONS * port, inputDescriptors_a800 + ATARI_JOYPAD_BUTTONS * port, (ATARI_JOYPAD_BUTTONS + 1) * sizeof(struct retro_input_descriptor)); + else if (device == RETRO_DEVICE_ATARI_5200_JOYSTICK) + memcpy(inputDescriptors_dyna + ATARI_JOYPAD_BUTTONS * port, inputDescriptors_a5200 + ATARI_JOYPAD_BUTTONS * port, (ATARI_JOYPAD_BUTTONS + 1) * sizeof(struct retro_input_descriptor)); + + environ_cb(RETRO_ENVIRONMENT_SET_INPUT_DESCRIPTORS, &inputDescriptors_dyna); } } @@ -636,21 +1144,11 @@ void retro_get_system_info(struct retro_system_info* info) #else info->library_version = "3.1.0"; #endif - info->valid_extensions = "xfd|atr|cdm|cas|bin|a52|zip|atx|car|rom|com|xex"; + info->valid_extensions = "xfd|atr|dcm|cas|bin|a52|zip|atx|car|rom|com|xex|m3u"; info->need_fullpath = true; info->block_extract = false; - } -/* -void retro_get_system_av_info(struct retro_system_av_info *info) -{ - struct retro_game_geometry geom = { retrow, retroh, 400, 300,4.0 / 3.0 }; - struct retro_system_timing timing = { retro_fps, 44100.0 }; - info->geometry = geom; - info->timing = timing; -} -*/ void retro_set_audio_sample(retro_audio_sample_t cb) { audio_cb = cb; @@ -679,6 +1177,8 @@ void retro_run(void) { bool updated = false; + libretro_runloop_active = 1; + if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE_UPDATE, &updated) && updated) update_variables(); @@ -713,59 +1213,77 @@ void retro_run(void) co_switch(emuThread); } -extern char Key_Sate[512]; + +extern char Key_State[512]; unsigned int lastdown, lastup, lastchar; static void keyboard_cb(bool down, unsigned keycode, uint32_t character, uint16_t mod) { if (keyboard_type == 0)return; - Key_Sate[keycode] = down ? 1 : 0; + Key_State[keycode] = down ? 1 : 0; /* printf( "Down: %s, Code: %d, Char: %u, Mod: %u.\n", down ? "yes" : "no", keycode, character, mod); */ - /* - if(down)lastdown=keycode; - else lastup=keycode; - lastchar=character; - */ -} - -int HandleExtension(char* path, char* ext) -{ - int len = strlen(path); - - if (len >= 4 && - path[len - 4] == '.' && - path[len - 3] == ext[0] && - path[len - 2] == ext[1] && - path[len - 1] == ext[2]) - { - return 1; - } - - return 0; } bool retro_load_game(const struct retro_game_info* info) { const char* full_path; + bool media_is_disk_tape = TRUE; (void)info; - struct retro_keyboard_callback cb = { keyboard_cb }; + environ_cb(RETRO_ENVIRONMENT_SET_KEYBOARD_CALLBACK, &cb); full_path = info->path; - strcpy(RPATH, full_path); + // If it's a m3u file + if (strendswith(full_path, "M3U")) + { + // Parse the m3u file + dc_parse_m3u(dc, full_path); + + // Some debugging + //log_cb(RETRO_LOG_INFO, "m3u file parsed, %d file(s) found\n", dc->count); + //for (unsigned i = 0; i < dc->count; i++) + //{ + // fprintf(fp, "file %d: %s\n", i + 1, dc->files[i]); + // log_cb(RETRO_LOG_INFO, "file %d: %s\n", i + 1, dc->files[i]); + //} + } + else if (strendswith(full_path, "XFD") || + strendswith(full_path, "ATR") || + strendswith(full_path, "DCM") || + strendswith(full_path, "ATX") || + strendswith(full_path, "CAS")) + { + // Add the file to disk control context + // Maybe, in a later version of retroarch, we could add disk on the fly (didn't find how to do this) + dc_add_file(dc, full_path); + } + else + media_is_disk_tape = FALSE; + + if (media_is_disk_tape) + { + // Init first disk + dc->index = 0; + dc->eject_state = false; + log_cb(RETRO_LOG_INFO, "Disk/Cassette (%d) inserted into drive 1 : %s\n", dc->index + 1, dc->files[dc->index]); + strcpy(RPATH, dc->files[0]); + } + else + strcpy(RPATH, full_path); update_variables(); - if (HandleExtension((char*)RPATH, "a52") || HandleExtension((char*)RPATH, "A52")) - autorun5200 = 1; + // Moved elsewhere + //if (HandleExtension((char*)RPATH, "a52") || HandleExtension((char*)RPATH, "A52")) + // autorunCartridge = 1; #ifdef RENDER16B memset(Retro_Screen, 0, 400 * 300 * 2); @@ -774,6 +1292,13 @@ bool retro_load_game(const struct retro_game_info* info) #endif memset(SNDBUF, 0, 1024 * 2 * 2); + /* trouble shooting */ + //int fileType = 0; + //char msg[256]; + //fileType = AFILE_DetectFileType(RPATH); + //sprintf(msg, "File type detected=%i\n", fileType); + //retro_message(msg, 10000, 0); + co_switch(emuThread); return true; @@ -799,23 +1324,51 @@ bool retro_load_game_special(unsigned type, const struct retro_game_info* info, size_t retro_serialize_size(void) { - return 0; + uint8_t* data_; + int size; + + data_ = calloc(A800_SAVE_STATE_SIZE, 1); + size = Retro_SaveAtariState(data_, A800_SAVE_STATE_SIZE, 1); + free(data_); + + //FILE* fp1 = fopen("E:\\debugme.txt", "a"); + //fprintf(fp1, "Estimated savestate size %i\n", size); + //fclose(fp1); + + return size; } bool retro_serialize(void* data_, size_t size) { - return false; + int returned_size; + + returned_size = Retro_SaveAtariState(data_, size, 1); + + //FILE* fp1 = fopen("E:\\debugme.txt", "a"); + //fprintf(fp1, "Actual savestate size %i\n", returned_size); + //fclose(fp1); + + return returned_size; } bool retro_unserialize(const void* data_, size_t size) { - return false; + int returned_size; + + returned_size = Retro_ReadAtariState(data_, size); + + //FILE* fp1 = fopen("E:\\debugme.txt", "a"); + //fprintf(fp1, "Savestate size read in %i\nFor game %s\n", returned_size,RPATH); + //fclose(fp1); + + return returned_size; } void* retro_get_memory_data(unsigned id) { if (id == RETRO_MEMORY_SYSTEM_RAM) return MEMORY_mem; + return NULL; } @@ -823,6 +1376,7 @@ size_t retro_get_memory_size(unsigned id) { if (id == RETRO_MEMORY_SYSTEM_RAM) return 65536; + return 0; } diff --git a/libretro/libretro-core.h b/libretro/libretro-core.h index 329e49b..dbb4a33 100644 --- a/libretro/libretro-core.h +++ b/libretro/libretro-core.h @@ -36,8 +36,7 @@ extern cothread_t mainThread; extern cothread_t emuThread; -extern char Key_Sate[512]; -extern char Key_Sate2[512]; +extern char Key_State[512]; extern int pauseg; @@ -76,4 +75,13 @@ extern int pauseg; #endif #define uint32 unsigned int #define uint8 unsigned char + +//Paddle & 5200 POT +#define LIBRETRO_ANALOG_RANGE 0x8000 +#define JOY_5200_MIN 6 +#define JOY_5200_MAX 220 +#define JOY_5200_CENTER 114 + +void retro_message(const char* text, unsigned int frames, int alt); +void retro_audio_cb(short l, short r); #endif diff --git a/libretro/libretro_core_options.h b/libretro/libretro_core_options.h new file mode 100644 index 0000000..88eb5eb --- /dev/null +++ b/libretro/libretro_core_options.h @@ -0,0 +1,736 @@ +#ifndef LIBRETRO_CORE_OPTIONS_H__ +#define LIBRETRO_CORE_OPTIONS_H__ + +#include +#include + +#include +#include + +#ifndef HAVE_NO_LANGEXTRA +#include "libretro_core_options_intl.h" +#endif + +/* + ******************************** + * VERSION: 1.0 + ******************************** + * + * - 1.0: First commit. Support for cor options v2 interfaec. + * - libretro_core_options_intl.h includes BOM and utf-8 + * fix for MSVC 2010-2013 + * - Contains HAVE_NO_LANGEXTRA flag to disable translations + * on platforms/compilers without BOM support + * - Uses core options v1 interface when + * RETRO_ENVIRONMENT_GET_CORE_OPTIONS_VERSION is >= 1 + * (previously required RETRO_ENVIRONMENT_GET_CORE_OPTIONS_VERSION == 1) + * - Support for generation of core options v0 retro_core_option_value + * arrays containing options with a single value + */ + +#ifdef __cplusplus +extern "C" { +#endif + +/* + ******************************** + * Core Option Definitions + ******************************** +*/ + +/* RETRO_LANGUAGE_ENGLISH */ + +/* Default language: + * - All other languages must include the same keys and values + * - Will be used as a fallback in the event that frontend language + * is not available + * - Will be used as a fallback for any missing entries in + * frontend language definition */ + +//struct retro_core_option_v2_category option_cats_us[] = { +// { NULL, NULL, NULL }, +//}; + +struct retro_core_option_v2_category option_cats_us[] = { + { + "video", + "Video", + "Configure video standard (NTSC/PAL). Enable Hi-res artifacting and set artifacting mode. Set internal resolution" + }, + { + "input", + "Input", + "Configure 5200 Digital and Analog Joystick sensitivity and Analog deadzone. Activate Swap or Dual Joysticks. Activate Paddle mode and set Paddle speed. Set retroarch keyboard type." + }, + { NULL, NULL, NULL }, +}; + +struct retro_core_option_v2_definition option_defs_us[] = { + { + "atari800_ntscpal", + "Video Standard", + NULL, + "Select whether emulated system is NTSC (60hz) or PAL (50hz)", + NULL, + "video", + { + { "NTSC", NULL }, + { "PAL", NULL }, + { NULL, NULL }, + }, + "disabled" + }, + { + "atari800_artifacting_mode", + "Hi-Res Artifacting Mode", + NULL, + "Set Hi-Res Artifacting mode used. Typically dependant on the actual emulated system. Pick the color combination that pleases you.", + NULL, + "video", + { + { "none", "None "}, + { "blue/brown 1", NULL }, + { "blue/brown 2", NULL }, + { "GTIA", NULL }, + { "CTIA", NULL }, + { NULL, NULL }, + }, + "none" + }, + { + "atari800_resolution", + "Internal resolution", + NULL, + "Set emulated systems internal resolution.", + NULL, + "video", + { + { "336x240", NULL }, + { "320x240", NULL }, + { "384x240", NULL }, + { "384x272", NULL }, + { "384x288", NULL }, + { "400x300", NULL }, + { NULL, NULL }, + }, + "336x240" + }, + { + "atari800_opt2", + "Controller Hacks", + NULL, + "Apply gamepad input hacks required for specific games. 'Dual Stick' maps Player 2's joystick to the right analog stick of Player 1's RetroPad, enabling dual stick control in 'Robotron 2084' and 'Space Dungeon'. 'Swap Ports' maps Player 1 to port 2 and Player 2 to port 1 of the emulated console, correcting the swapped inputs of 'Wizard of Wor', 'Apple Panic' and a few other games", + NULL, + "input", + { + { "none", NULL }, + { "enabled", "Dual Stick" }, + { "Swap Ports", "Swap Ports" }, + { NULL, NULL }, + }, + "none" + }, + { + "paddle_active", + "Activate Paddle Mode", + NULL, + "Use analog stick and digital pad to control paddle games. Dual Stick and Swap Ports will be de-activated if this is enabled", + NULL, + "input", + { + { "disabled", NULL }, + { "enabled", NULL }, + { NULL, NULL }, + }, + "disabled" + }, + { + "paddle_movement_speed", + "Paddle Movement Speed", + NULL, + "Determines how fast the paddle will move.", + NULL, + "input", + { + { "1", NULL }, + { "2", NULL }, + { "3", NULL }, + { "4", NULL }, + { "5", NULL }, + { "6", NULL }, + { "7", NULL }, + { "8", NULL }, + { "9", NULL }, + { NULL, NULL }, + }, + "3" + }, + { + "pot_digital_sensitivity", + "Digital Joystick Sensitivity", + NULL, + "Set the effective range of the emulated analog joystick when using the gamepad's digital D-Pad for movement. Lower values equate to slower speeds. Also can be used to make games like Centipede, Kaboom, Missile Command, Pengo, Realsports Tennis, Pole Position, Xari Arena and Super Breakout more playable.", + NULL, + "input", + { + { "5", "5%" }, + { "10", "10%" }, + { "15", "15%" }, + { "20", "20%" }, + { "25", "25%" }, + { "30", "30%" }, + { "35", "35%" }, + { "40", "40%" }, + { "45", "45%" }, + { "50", "50%" }, + { "55", "55%" }, + { "60", "60%" }, + { "65", "65%" }, + { "70", "70%" }, + { "75", "75%" }, + { "80", "80%" }, + { "85", "85%" }, + { "90", "90%" }, + { "95", "95%" }, + { "100", "100%" }, + { NULL, NULL }, + }, + "100" + }, + { + "pot_analog_sensitivity", + "Analog Joystick Sensitivity", + NULL, + "Set the effective range of the emulated analog joystick when using the gamepad's left analog stick for movement. Lower values equate to slower speeds. Also can be used to make games like Centipede, Kaboom, Missile Command, Pengo, Realsports Tennis, Pole Position, Xari Arena and Super Breakout more playable.", + NULL, + "input", + { + { "5", "5%" }, + { "10", "10%" }, + { "15", "15%" }, + { "20", "20%" }, + { "25", "25%" }, + { "30", "30%" }, + { "35", "35%" }, + { "40", "40%" }, + { "45", "45%" }, + { "50", "50%" }, + { "55", "55%" }, + { "60", "60%" }, + { "65", "65%" }, + { "70", "70%" }, + { "75", "75%" }, + { "80", "80%" }, + { "85", "85%" }, + { "90", "90%" }, + { "95", "95%" }, + { "100", "100%" }, + { NULL, NULL }, + }, + "100" + }, + { + "pot_analog_deadzone", + "Analog Joystick Deadzone", + NULL, + "Set the deadzone of the gamepad's analog sticks. Use this to eliminate controller drift.", + NULL, + "input", + { + { "0", "0%" }, + { "3", "3%" }, + { "5", "5%" }, + { "7", "7%" }, + { "10", "10%" }, + { "13", "13%" }, + { "15", "15%" }, + { "17", "17%" }, + { "20", "20%" }, + { "23", "23%" }, + { "25", "25%" }, + { "27", "27%" }, + { "30", "30%" }, + { NULL, NULL }, + }, + "15" + }, + { + "atari800_keyboard", + "Retroarch Keyboard type", + NULL, + "Set whether keyboard is polled or used callback.", + NULL, + "input", + { + { "poll", NULL }, + { "callback", NULL }, + { NULL, NULL }, + }, + "poll" + }, + { + "keyboard_defines", + "Atari Keyboard Defines", + NULL, + "Joystick and Console Key mappings for\n\"Atari Keyboard\".\n \n Joystick \"Keyboard Numpad 8, 2, 4 & 6\".\n \n5200 analog joystick. Left analog stick or \"Keyboard Numpad 8, 2, 4 & 6\".\n \nFire 1 \"Right CTRL\". Fire 2 \"Right ALT\".\n \n5200 * \"Keyboard Numpad * \". # \"Keyboard =\"\n \n5200 Keypad 0-9 \"Keyboard 0-9\".\nPause \"P\", Reset \"R\".\n \n'Console' keys Option \"F2\", Select \"F3\",\nStart \"F4\".\n \nController 1 only.", + NULL, + "input", + { + { "informational", NULL }, + { NULL, NULL }, + }, + "informational" + }, + { + "atari800_system", + "Atari System", + NULL, + "Select system emulated. Atari 5200 for Atari 5200 console games. 400/800 (OS B) for <48K games or ones that require OS/B. 800XL (64K) works for most content. 130XE (128K), Modern XL/XE(320K Compy Shop), Modern XL/XE(576K), Modern XL/XE(1088K) for content that needs more than 64K.", + NULL, + NULL, + { + { "400/800 (OS B)", "Atari 400/800 (OS B)" }, + { "800XL (64K)", "Atari 800XL (64K)" }, + { "130XE (128K)", "Atari 130XE (128K)" }, + { "Modern XL/XE(320K CS)", "Modern Atari XL/XE(320K Compy Shop)" }, + { "Modern XL/XE(576K)", "Modern Atari XL/XE(576K)" }, + { "Modern XL/XE(1088K)", "Modern Atari XL/XE(1088K)" }, + { "5200", "Atari 5200 Super System" }, + { NULL, NULL }, + }, + "400/800 (OS B)" + }, + { + "atari800_internalbasic", + "Internal BASIC (hold OPTION on boot) (Restart)", + NULL, + "Enable for content that needs Atari BASIC in order to run. A proper ROM file (ATARIBAS.ROM) is needed.", + NULL, + NULL, + { + { "disabled", NULL }, + { "enabled", NULL }, + { NULL, NULL }, + }, + "disabled" + }, + { + "atari800_sioaccel", + "SIO Acceleration", + NULL, + "This enables ALL SIO acceleration. Enabled improves loading speed for Disk and Cassette images. Disable only for protected disk (.ATX) and protected cassette images. Reboot required if change made while loading a cassette image.", + NULL, + NULL, + { + { "disabled", NULL }, + { "enabled", NULL }, + { NULL, NULL }, + }, + "enabled" + }, + { + "atari800_cassboot", + "Boot from Cassette (Reboot)", + NULL, + "Forces emulated system to boot from autoboot cassette images by holding down the \"START\" key on boot.", + NULL, + NULL, + { + { "disabled", NULL }, + { "enabled", NULL }, + { NULL, NULL }, + }, + "disabled" + }, + { + "atari800_opt1", + "Autodetect Atari Cartridge Type (Restart)", + NULL, + "Emulator will auto detect Atari cartridge based on checksum. (requires good ROM dumps).", + NULL, + NULL, + { + { "disabled", NULL }, + { "enabled", NULL }, + { NULL, NULL }, + }, + "disabled" + }, + /* Not sure if I want to implement this. Only helps a small handful of games ( Kaboom, Super Breakout and Star Wars The Arcade Game + { + "pot_analog_center", + "Analog Joystick/Paddle Center", + NULL, + "Set the value for emulated analog joystick or paddle center. Use this to properly center player for some games like Kaboom, Starwars or Super Breakout. Default is 114", + NULL, + "5200", + { + { "80", NULL }, + { "82", NULL }, + { "84", NULL }, + { "86", NULL }, + { "88", NULL }, + { "90", NULL }, + { "92", NULL }, + { "94", NULL }, + { "96", NULL }, + { "98", NULL }, + { "100", NULL }, + { "102", NULL }, + { "104", NULL }, + { "106", NULL }, + { "108", NULL }, + { "110", NULL }, + { "112", NULL }, + { "114", NULL }, + { "116", NULL }, + { "118", NULL }, + { "120", NULL }, + { "122", NULL }, + { "124", NULL }, + { "126", NULL }, + { "128", NULL }, + { "130", NULL }, + { "132", NULL }, + { "134", NULL }, + { "136", NULL }, + { "130", NULL }, + { "140", NULL }, + { "142", NULL }, + { "144", NULL }, + { "146", NULL }, + { "148", NULL }, + { "150", NULL }, + { NULL, NULL }, + }, + "114" + }, */ + { NULL, NULL, NULL, NULL, NULL, NULL, {{0}}, NULL }, +}; + +struct retro_core_options_v2 options_us = { + option_cats_us, + option_defs_us +}; + +/* + ******************************** + * Language Mapping + ******************************** +*/ + +#ifndef HAVE_NO_LANGEXTRA +struct retro_core_options_v2 *options_intl[RETRO_LANGUAGE_LAST] = { + &options_us, /* RETRO_LANGUAGE_ENGLISH */ + NULL, /* RETRO_LANGUAGE_JAPANESE */ + NULL, /* RETRO_LANGUAGE_FRENCH */ + NULL, /* RETRO_LANGUAGE_SPANISH */ + NULL, /* RETRO_LANGUAGE_GERMAN */ + NULL, /* RETRO_LANGUAGE_ITALIAN */ + NULL, /* RETRO_LANGUAGE_DUTCH */ + NULL, /* RETRO_LANGUAGE_PORTUGUESE_BRAZIL */ + NULL, /* RETRO_LANGUAGE_PORTUGUESE_PORTUGAL */ + NULL, /* RETRO_LANGUAGE_RUSSIAN */ + NULL, /* RETRO_LANGUAGE_KOREAN */ + NULL, /* RETRO_LANGUAGE_CHINESE_TRADITIONAL */ + NULL, /* RETRO_LANGUAGE_CHINESE_SIMPLIFIED */ + NULL, /* RETRO_LANGUAGE_ESPERANTO */ + NULL, /* RETRO_LANGUAGE_POLISH */ + NULL, /* RETRO_LANGUAGE_VIETNAMESE */ + NULL, /* RETRO_LANGUAGE_ARABIC */ + NULL, /* RETRO_LANGUAGE_GREEK */ + NULL, /* RETRO_LANGUAGE_TURKISH */ + NULL, /* RETRO_LANGUAGE_SLOVAK */ + NULL, /* RETRO_LANGUAGE_PERSIAN */ + NULL, /* RETRO_LANGUAGE_HEBREW */ + NULL, /* RETRO_LANGUAGE_ASTURIAN */ + NULL, /* RETRO_LANGUAGE_FINNISH */ +}; +#endif + +/* + ******************************** + * Functions + ******************************** +*/ + +/* Handles configuration/setting of core options. + * Should be called as early as possible - ideally inside + * retro_set_environment(), and no later than retro_load_game() + * > We place the function body in the header to avoid the + * necessity of adding more .c files (i.e. want this to + * be as painless as possible for core devs) + */ + +static INLINE void libretro_set_core_options(retro_environment_t environ_cb, + bool *categories_supported) +{ + unsigned version = 0; +#ifndef HAVE_NO_LANGEXTRA + unsigned language = 0; +#endif + + if (!environ_cb || !categories_supported) + return; + + *categories_supported = false; + + if (!environ_cb(RETRO_ENVIRONMENT_GET_CORE_OPTIONS_VERSION, &version)) + version = 0; + + if (version >= 2) + { +#ifndef HAVE_NO_LANGEXTRA + struct retro_core_options_v2_intl core_options_intl; + + core_options_intl.us = &options_us; + core_options_intl.local = NULL; + + if (environ_cb(RETRO_ENVIRONMENT_GET_LANGUAGE, &language) && + (language < RETRO_LANGUAGE_LAST) && (language != RETRO_LANGUAGE_ENGLISH)) + core_options_intl.local = options_intl[language]; + + *categories_supported = environ_cb(RETRO_ENVIRONMENT_SET_CORE_OPTIONS_V2_INTL, + &core_options_intl); +#else + *categories_supported = environ_cb(RETRO_ENVIRONMENT_SET_CORE_OPTIONS_V2, + &options_us); +#endif + } + else + { + size_t i, j; + size_t option_index = 0; + size_t num_options = 0; + struct retro_core_option_definition + *option_v1_defs_us = NULL; +#ifndef HAVE_NO_LANGEXTRA + size_t num_options_intl = 0; + struct retro_core_option_v2_definition + *option_defs_intl = NULL; + struct retro_core_option_definition + *option_v1_defs_intl = NULL; + struct retro_core_options_intl + core_options_v1_intl; +#endif + struct retro_variable *variables = NULL; + char **values_buf = NULL; + + /* Determine total number of options */ + while (true) + { + if (option_defs_us[num_options].key) + num_options++; + else + break; + } + + if (version >= 1) + { + /* Allocate US array */ + option_v1_defs_us = (struct retro_core_option_definition *) + calloc(num_options + 1, sizeof(struct retro_core_option_definition)); + + /* Copy parameters from option_defs_us array */ + for (i = 0; i < num_options; i++) + { + struct retro_core_option_v2_definition *option_def_us = &option_defs_us[i]; + struct retro_core_option_value *option_values = option_def_us->values; + struct retro_core_option_definition *option_v1_def_us = &option_v1_defs_us[i]; + struct retro_core_option_value *option_v1_values = option_v1_def_us->values; + + option_v1_def_us->key = option_def_us->key; + option_v1_def_us->desc = option_def_us->desc; + option_v1_def_us->info = option_def_us->info; + option_v1_def_us->default_value = option_def_us->default_value; + + /* Values must be copied individually... */ + while (option_values->value) + { + option_v1_values->value = option_values->value; + option_v1_values->label = option_values->label; + + option_values++; + option_v1_values++; + } + } + +#ifndef HAVE_NO_LANGEXTRA + if (environ_cb(RETRO_ENVIRONMENT_GET_LANGUAGE, &language) && + (language < RETRO_LANGUAGE_LAST) && (language != RETRO_LANGUAGE_ENGLISH) && + options_intl[language]) + option_defs_intl = options_intl[language]->definitions; + + if (option_defs_intl) + { + /* Determine number of intl options */ + while (true) + { + if (option_defs_intl[num_options_intl].key) + num_options_intl++; + else + break; + } + + /* Allocate intl array */ + option_v1_defs_intl = (struct retro_core_option_definition *) + calloc(num_options_intl + 1, sizeof(struct retro_core_option_definition)); + + /* Copy parameters from option_defs_intl array */ + for (i = 0; i < num_options_intl; i++) + { + struct retro_core_option_v2_definition *option_def_intl = &option_defs_intl[i]; + struct retro_core_option_value *option_values = option_def_intl->values; + struct retro_core_option_definition *option_v1_def_intl = &option_v1_defs_intl[i]; + struct retro_core_option_value *option_v1_values = option_v1_def_intl->values; + + option_v1_def_intl->key = option_def_intl->key; + option_v1_def_intl->desc = option_def_intl->desc; + option_v1_def_intl->info = option_def_intl->info; + option_v1_def_intl->default_value = option_def_intl->default_value; + + /* Values must be copied individually... */ + while (option_values->value) + { + option_v1_values->value = option_values->value; + option_v1_values->label = option_values->label; + + option_values++; + option_v1_values++; + } + } + } + + core_options_v1_intl.us = option_v1_defs_us; + core_options_v1_intl.local = option_v1_defs_intl; + + environ_cb(RETRO_ENVIRONMENT_SET_CORE_OPTIONS_INTL, &core_options_v1_intl); +#else + environ_cb(RETRO_ENVIRONMENT_SET_CORE_OPTIONS, option_v1_defs_us); +#endif + } + else + { + /* Allocate arrays */ + variables = (struct retro_variable *)calloc(num_options + 1, + sizeof(struct retro_variable)); + values_buf = (char **)calloc(num_options, sizeof(char *)); + + if (!variables || !values_buf) + goto error; + + /* Copy parameters from option_defs_us array */ + for (i = 0; i < num_options; i++) + { + const char *key = option_defs_us[i].key; + const char *desc = option_defs_us[i].desc; + const char *default_value = option_defs_us[i].default_value; + struct retro_core_option_value *values = option_defs_us[i].values; + size_t buf_len = 3; + size_t default_index = 0; + + values_buf[i] = NULL; + + if (desc) + { + size_t num_values = 0; + + /* Determine number of values */ + while (true) + { + if (values[num_values].value) + { + /* Check if this is the default value */ + if (default_value) + if (strcmp(values[num_values].value, default_value) == 0) + default_index = num_values; + + buf_len += strlen(values[num_values].value); + num_values++; + } + else + break; + } + + /* Build values string */ + if (num_values > 0) + { + buf_len += num_values - 1; + buf_len += strlen(desc); + + values_buf[i] = (char *)calloc(buf_len, sizeof(char)); + if (!values_buf[i]) + goto error; + + strcpy(values_buf[i], desc); + strcat(values_buf[i], "; "); + + /* Default value goes first */ + strcat(values_buf[i], values[default_index].value); + + /* Add remaining values */ + for (j = 0; j < num_values; j++) + { + if (j != default_index) + { + strcat(values_buf[i], "|"); + strcat(values_buf[i], values[j].value); + } + } + } + } + + variables[option_index].key = key; + variables[option_index].value = values_buf[i]; + option_index++; + } + + /* Set variables */ + environ_cb(RETRO_ENVIRONMENT_SET_VARIABLES, variables); + } + +error: + /* Clean up */ + + if (option_v1_defs_us) + { + free(option_v1_defs_us); + option_v1_defs_us = NULL; + } + +#ifndef HAVE_NO_LANGEXTRA + if (option_v1_defs_intl) + { + free(option_v1_defs_intl); + option_v1_defs_intl = NULL; + } +#endif + + if (values_buf) + { + for (i = 0; i < num_options; i++) + { + if (values_buf[i]) + { + free(values_buf[i]); + values_buf[i] = NULL; + } + } + + free(values_buf); + values_buf = NULL; + } + + if (variables) + { + free(variables); + variables = NULL; + } + } +} + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libretro/libretro_core_options_intl.h b/libretro/libretro_core_options_intl.h new file mode 100644 index 0000000..038553a --- /dev/null +++ b/libretro/libretro_core_options_intl.h @@ -0,0 +1,91 @@ +#ifndef LIBRETRO_CORE_OPTIONS_INTL_H__ +#define LIBRETRO_CORE_OPTIONS_INTL_H__ + +#if defined(_MSC_VER) && (_MSC_VER >= 1500 && _MSC_VER < 1900) +/* https://support.microsoft.com/en-us/kb/980263 */ +#pragma execution_character_set("utf-8") +#pragma warning(disable:4566) +#endif + +#include + +/* + ******************************** + * VERSION: 2.0 + ******************************** + * + * - 2.0: Add support for core options v2 interface + * - 1.3: Move translations to libretro_core_options_intl.h + * - libretro_core_options_intl.h includes BOM and utf-8 + * fix for MSVC 2010-2013 + * - Added HAVE_NO_LANGEXTRA flag to disable translations + * on platforms/compilers without BOM support + * - 1.2: Use core options v1 interface when + * RETRO_ENVIRONMENT_GET_CORE_OPTIONS_VERSION is >= 1 + * (previously required RETRO_ENVIRONMENT_GET_CORE_OPTIONS_VERSION == 1) + * - 1.1: Support generation of core options v0 retro_core_option_value + * arrays containing options with a single value + * - 1.0: First commit +*/ + +#ifdef __cplusplus +extern "C" { +#endif + +/* + ******************************** + * Core Option Definitions + ******************************** +*/ + +/* RETRO_LANGUAGE_JAPANESE */ + +/* RETRO_LANGUAGE_FRENCH */ + +/* RETRO_LANGUAGE_SPANISH */ + +/* RETRO_LANGUAGE_GERMAN */ + +/* RETRO_LANGUAGE_ITALIAN */ + +/* RETRO_LANGUAGE_DUTCH */ + +/* RETRO_LANGUAGE_PORTUGUESE_BRAZIL */ + +/* RETRO_LANGUAGE_PORTUGUESE_PORTUGAL */ + +/* RETRO_LANGUAGE_RUSSIAN */ + +/* RETRO_LANGUAGE_KOREAN */ + +/* RETRO_LANGUAGE_CHINESE_TRADITIONAL */ + +/* RETRO_LANGUAGE_CHINESE_SIMPLIFIED */ + +/* RETRO_LANGUAGE_ESPERANTO */ + +/* RETRO_LANGUAGE_POLISH */ + +/* RETRO_LANGUAGE_VIETNAMESE */ + +/* RETRO_LANGUAGE_ARABIC */ + +/* RETRO_LANGUAGE_GREEK */ + +/* RETRO_LANGUAGE_TURKISH */ + +/* RETRO_LANGUAGE_SLOVAK */ + +/* RETRO_LANGUAGE_PERSIAN */ + +/* RETRO_LANGUAGE_HEBREW */ + +/* RETRO_LANGUAGE_ASTURIAN */ + +/* RETRO_LANGUAGE_FINNISH */ + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libretro/platform.c b/libretro/platform.c index c7bda7b..ea4d8f2 100644 --- a/libretro/platform.c +++ b/libretro/platform.c @@ -37,24 +37,31 @@ #include "screen.h" #include "colours.h" #include "ui.h" - -extern char Key_Sate[512]; - #include "libretro-core.h" #include "libretro.h" #include "retroscreen.h" +#define RETRO_DEVICE_ATARI_KEYBOARD RETRO_DEVICE_SUBCLASS(RETRO_DEVICE_KEYBOARD, 0) +#define RETRO_DEVICE_ATARI_JOYSTICK RETRO_DEVICE_SUBCLASS(RETRO_DEVICE_JOYPAD, 1) +#define RETRO_DEVICE_ATARI_5200_JOYSTICK RETRO_DEVICE_SUBCLASS(RETRO_DEVICE_JOYPAD, 2) + +extern char Key_State[512]; extern int SHIFTON; extern int CTRLON; extern int UI_is_active; +extern int SHOWKEY, SHOWKEYDELAY; static int swap_joysticks = FALSE; int PLATFORM_kbd_joy_0_enabled = TRUE; /* enabled by default, doesn't hurt */ int PLATFORM_kbd_joy_1_enabled = TRUE;//FALSE; /* disabled, would steal normal keys */ int PLATFORM_kbd_joy_2_enabled = TRUE;//FALSE; /* disabled, would steal normal keys */ int PLATFORM_kbd_joy_3_enabled = TRUE;//FALSE; /* disabled, would steal normal keys */ + extern unsigned char MXjoy[4]; // joy -extern int mbt[16]; +extern int mbt[4][16]; +extern unsigned atari_devices[4]; +extern int paddle_mode; + extern int retro_sound_finalized; int CURRENT_TV=Atari800_TV_PAL; @@ -164,45 +171,46 @@ static int key_control = 0; int PLATFORM_Keyboard(void) { int shiftctrl = 0; + int whichButton = 0; UI_alt_function = -1; - if (Key_Sate[RETROK_LALT]){ + if (Key_State[RETROK_LALT]){ - if (Key_Sate[RETROK_r]) + if (Key_State[RETROK_r]) UI_alt_function = UI_MENU_RUN; - else if (Key_Sate[RETROK_y]) + else if (Key_State[RETROK_y]) UI_alt_function = UI_MENU_SYSTEM; - else if (Key_Sate[RETROK_o]) + else if (Key_State[RETROK_o]) UI_alt_function = UI_MENU_SOUND; - else if (Key_Sate[RETROK_w]) + else if (Key_State[RETROK_w]) UI_alt_function = UI_MENU_SOUND_RECORDING; - else if (Key_Sate[RETROK_a]) + else if (Key_State[RETROK_a]) UI_alt_function = UI_MENU_ABOUT; - else if (Key_Sate[RETROK_s]) + else if (Key_State[RETROK_s]) UI_alt_function = UI_MENU_SAVESTATE; - else if (Key_Sate[RETROK_d]) + else if (Key_State[RETROK_d]) UI_alt_function = UI_MENU_DISK; - else if (Key_Sate[RETROK_l]) + else if (Key_State[RETROK_l]) UI_alt_function = UI_MENU_LOADSTATE; - else if (Key_Sate[RETROK_c]) + else if (Key_State[RETROK_c]) UI_alt_function = UI_MENU_CARTRIDGE; - else if (Key_Sate[RETROK_t]) + else if (Key_State[RETROK_t]) UI_alt_function = UI_MENU_CASSETTE; - else if (Key_Sate[RETROK_BACKSLASH]) + else if (Key_State[RETROK_BACKSLASH]) return AKEY_PBI_BB_MENU; } /* SHIFT STATE */ - //if ((Key_Sate[RETROK_LSHIFT]) || (Key_Sate[RETROK_RSHIFT])) + //if ((Key_State[RETROK_LSHIFT]) || (Key_State[RETROK_RSHIFT])) if (SHIFTON == 1) INPUT_key_shift = 1; else INPUT_key_shift = 0; /* CONTROL STATE */ - //if ((Key_Sate[RETROK_LCTRL]) || (Key_Sate[RETROK_RCTRL])) + //if ((Key_State[RETROK_LCTRL]) || (Key_State[RETROK_RCTRL])) if (CTRLON == 1) key_control = 1; else @@ -212,21 +220,21 @@ int PLATFORM_Keyboard(void) /* OPTION / SELECT / START keys */ INPUT_key_consol = INPUT_CONSOL_NONE; - if (Key_Sate[RETROK_F2]) + if (Key_State[RETROK_F2]) INPUT_key_consol &= (~INPUT_CONSOL_OPTION); - if (Key_Sate[RETROK_F3]) + if (Key_State[RETROK_F3]) INPUT_key_consol &= (~INPUT_CONSOL_SELECT); - if (Key_Sate[RETROK_F4]) + if (Key_State[RETROK_F4]) INPUT_key_consol &= (~INPUT_CONSOL_START); /* Handle movement and special keys. */ - if (Key_Sate[RETROK_F1])return AKEY_UI; + if (Key_State[RETROK_F1]) return AKEY_UI; - if (Key_Sate[RETROK_F5]) + if (Key_State[RETROK_F5] && (Atari800_machine_type != Atari800_MACHINE_5200)) return INPUT_key_shift ? AKEY_COLDSTART : AKEY_WARMSTART; - if (Key_Sate[RETROK_F12])return AKEY_TURBO; + if (Key_State[RETROK_F12]) return AKEY_TURBO; if (UI_alt_function != -1) { return AKEY_UI; @@ -235,69 +243,131 @@ int PLATFORM_Keyboard(void) if (INPUT_key_shift) shiftctrl ^= AKEY_SHFT; - if (Atari800_machine_type == Atari800_MACHINE_5200 && !UI_is_active) { + // Atari 5200 specific. Don't accept joypad input when UI is active. or virtual keyboard is active. + if (Atari800_machine_type == Atari800_MACHINE_5200 && !UI_is_active) + { + INPUT_key_shift = 0; - if (MXjoy[0]&0x40) { /* 2nd action button */ - INPUT_key_shift = 1; - } - else { - INPUT_key_shift = 0; + if (SHOWKEYDELAY) + SHOWKEYDELAY--; + + /* Don't accept joypad input when virtual keyboard is active */ + for (int i = 0; i < 4; i++) + { + if (SHOWKEY == -1) + { + //if (MXjoy[0]&0x40) { + /* 2nd action button */ + if (atari_devices[i] == RETRO_DEVICE_ATARI_JOYSTICK) + whichButton = RETRO_DEVICE_ID_JOYPAD_X; + else if (atari_devices[i] == RETRO_DEVICE_ATARI_5200_JOYSTICK) + whichButton = RETRO_DEVICE_ID_JOYPAD_A; + else if (atari_devices[i] == RETRO_DEVICE_ATARI_KEYBOARD && (Key_State[RETROK_RCTRL])) + INPUT_key_shift = 1; + + if (mbt[i][whichButton]) { + INPUT_key_shift = 1; + break; + } + + /* shared between ATARI_JOYSTICK and ATARI_5200*/ + if (mbt[i][RETRO_DEVICE_ID_JOYPAD_START]) + return AKEY_5200_START ^ shiftctrl; + + /* only Hash # and Asterick * */ + if (atari_devices[i] == RETRO_DEVICE_ATARI_JOYSTICK) + { + if (mbt[i][RETRO_DEVICE_ID_JOYPAD_SELECT]) return AKEY_5200_HASH ^ shiftctrl; + if (mbt[i][RETRO_DEVICE_ID_JOYPAD_L]) return AKEY_5200_ASTERISK ^ shiftctrl; + } + /* We map most all keys. Only number keys done are 0-4. This might change */ + /* Not a big fan of this method. Likely creates issues on 2 or more player games. */ + /* But testing seems to show otherwise. Maybe OR the values and then return the result */ + else if (atari_devices[i] == RETRO_DEVICE_ATARI_5200_JOYSTICK) + { + if (mbt[i][RETRO_DEVICE_ID_JOYPAD_Y]) return AKEY_5200_HASH ^ shiftctrl; + if (mbt[i][RETRO_DEVICE_ID_JOYPAD_X]) return AKEY_5200_ASTERISK ^ shiftctrl; + if (mbt[i][RETRO_DEVICE_ID_JOYPAD_SELECT]) return AKEY_5200_PAUSE ^ shiftctrl; + if (mbt[i][RETRO_DEVICE_ID_JOYPAD_L]) return AKEY_5200_0 ^ shiftctrl; + if (mbt[i][RETRO_DEVICE_ID_JOYPAD_R]) return AKEY_5200_1 ^ shiftctrl; + if (mbt[i][RETRO_DEVICE_ID_JOYPAD_L2]) return AKEY_5200_2 ^ shiftctrl; + if (mbt[i][RETRO_DEVICE_ID_JOYPAD_R2]) return AKEY_5200_3 ^ shiftctrl; + if (mbt[i][RETRO_DEVICE_ID_JOYPAD_L3]) return AKEY_5200_7 ^ shiftctrl; + } + } + + if (atari_devices[i] == RETRO_DEVICE_ATARI_JOYSTICK) + whichButton = RETRO_DEVICE_ID_JOYPAD_L3; + else if (atari_devices[i] == RETRO_DEVICE_ATARI_5200_JOYSTICK) + whichButton = RETRO_DEVICE_ID_JOYPAD_R3; + + if (mbt[i][whichButton]) + if (!SHOWKEYDELAY) + { + SHOWKEY = -SHOWKEY; + SHOWKEYDELAY = 20; + } } - if (mbt[RETRO_DEVICE_ID_JOYPAD_START]) - return AKEY_5200_START ^ shiftctrl; - - if (Key_Sate[RETROK_F4]) - return AKEY_5200_START ^ shiftctrl; - - if(Key_Sate[RETROK_p])return AKEY_5200_PAUSE ^ shiftctrl; - if(Key_Sate[RETROK_r])return AKEY_5200_RESET ^ shiftctrl; - if(Key_Sate[RETROK_0])return AKEY_5200_0 ^ shiftctrl; - if(Key_Sate[RETROK_1])return AKEY_5200_1 ^ shiftctrl; - if(Key_Sate[RETROK_2])return AKEY_5200_2 ^ shiftctrl; - if(Key_Sate[RETROK_3])return AKEY_5200_3 ^ shiftctrl; - if(Key_Sate[RETROK_4])return AKEY_5200_4 ^ shiftctrl; - if(Key_Sate[RETROK_5])return AKEY_5200_5 ^ shiftctrl; - if(Key_Sate[RETROK_6])return AKEY_5200_6 ^ shiftctrl; - if(Key_Sate[RETROK_7])return AKEY_5200_7 ^ shiftctrl; - if(Key_Sate[RETROK_8])return AKEY_5200_8 ^ shiftctrl; - if(Key_Sate[RETROK_9])return AKEY_5200_9 ^ shiftctrl; - if(Key_Sate[RETROK_HASH])return AKEY_5200_HASH ^ shiftctrl; - if(Key_Sate[RETROK_EQUALS])return AKEY_5200_HASH ^ shiftctrl; - if(Key_Sate[RETROK_ASTERISK])return AKEY_5200_ASTERISK ^ shiftctrl; - if(Key_Sate[RETROK_KP_MULTIPLY])return AKEY_5200_ASTERISK ^ shiftctrl; - + /* keyboard mapped to 5200 controls*/ + if (Key_State[RETROK_F4]) return AKEY_5200_START ^ shiftctrl; + if (Key_State[RETROK_p]) return AKEY_5200_PAUSE ^ shiftctrl; + if (Key_State[RETROK_r]) return AKEY_5200_RESET ^ shiftctrl; + if (Key_State[RETROK_0]) return AKEY_5200_0 ^ shiftctrl; + if (Key_State[RETROK_1]) return AKEY_5200_1 ^ shiftctrl; + if (Key_State[RETROK_2]) return AKEY_5200_2 ^ shiftctrl; + if (Key_State[RETROK_3]) return AKEY_5200_3 ^ shiftctrl; + if (Key_State[RETROK_4]) return AKEY_5200_4 ^ shiftctrl; + if (Key_State[RETROK_5]) return AKEY_5200_5 ^ shiftctrl; + if (Key_State[RETROK_6]) return AKEY_5200_6 ^ shiftctrl; + if (Key_State[RETROK_7]) return AKEY_5200_7 ^ shiftctrl; + if (Key_State[RETROK_8]) return AKEY_5200_8 ^ shiftctrl; + if (Key_State[RETROK_9]) return AKEY_5200_9 ^ shiftctrl; + if (Key_State[RETROK_HASH]) return AKEY_5200_HASH ^ shiftctrl; + if (Key_State[RETROK_EQUALS]) return AKEY_5200_HASH ^ shiftctrl; + if (Key_State[RETROK_ASTERISK]) return AKEY_5200_ASTERISK ^ shiftctrl; + if (Key_State[RETROK_KP_MULTIPLY]) return AKEY_5200_ASTERISK ^ shiftctrl; + + /* virtual keyboard mapped to 5200 controls */ + if (Key_State[RETROK_F5]) + return AKEY_5200_RESET ^ shiftctrl; + + if (Key_State[RETROK_F7]) + { + SHOWKEY = -1; + SHOWKEYDELAY = 20; + return AKEY_UI; + } return AKEY_NONE; } //else if (Atari800_machine_type != Atari800_MACHINE_5200 && !UI_is_active) -{ if (key_control) shiftctrl ^= AKEY_CTRL; - if (Key_Sate[RETROK_BACKQUOTE] || Key_Sate[RETROK_LSUPER] ) + if (Key_State[RETROK_BACKQUOTE] || Key_State[RETROK_LSUPER] ) return AKEY_ATARI ^ shiftctrl; - if (Key_Sate[RETROK_RSUPER] ){ + if (Key_State[RETROK_RSUPER] ){ if (INPUT_key_shift) return AKEY_CAPSLOCK; else return AKEY_CAPSTOGGLE; } - if (Key_Sate[RETROK_END] || Key_Sate[RETROK_F6] ) + if (Key_State[RETROK_END] || Key_State[RETROK_F6] ) return AKEY_HELP ^ shiftctrl; - if (Key_Sate[RETROK_PAGEDOWN]) + if (Key_State[RETROK_PAGEDOWN]) return AKEY_F2 | AKEY_SHFT; - if (Key_Sate[RETROK_PAGEUP]) + if (Key_State[RETROK_PAGEUP]) return AKEY_F1 | AKEY_SHFT; - if (Key_Sate[RETROK_HOME]) + if (Key_State[RETROK_HOME]) return key_control ? AKEY_LESS|shiftctrl : AKEY_CLEAR; - if (Key_Sate[RETROK_PAUSE]) + if (Key_State[RETROK_PAUSE]) { if (BINLOAD_wait_active) { BINLOAD_pause_loading = TRUE; @@ -306,17 +376,17 @@ int PLATFORM_Keyboard(void) else return AKEY_BREAK; } - if (Key_Sate[RETROK_CAPSLOCK]){ + if (Key_State[RETROK_CAPSLOCK]){ if (INPUT_key_shift) return AKEY_CAPSLOCK|shiftctrl; else return AKEY_CAPSTOGGLE|shiftctrl; } - if (Key_Sate[RETROK_SPACE]) + if (Key_State[RETROK_SPACE]) return AKEY_SPACE ^ shiftctrl; - if (Key_Sate[RETROK_BACKSPACE]){ + if (Key_State[RETROK_BACKSPACE]){ if (INPUT_key_shift) return AKEY_DELETE_CHAR; else if (key_control) @@ -325,32 +395,32 @@ int PLATFORM_Keyboard(void) return AKEY_BACKSPACE; } - if (Key_Sate[RETROK_RETURN]) + if (Key_State[RETROK_RETURN]) return AKEY_RETURN ^ shiftctrl; - if (Key_Sate[RETROK_LEFT]) + if (Key_State[RETROK_LEFT]) return (!UI_is_active && Atari800_f_keys ? AKEY_F3 : (INPUT_key_shift ? AKEY_PLUS : AKEY_LEFT)) ^ shiftctrl; - if (Key_Sate[RETROK_RIGHT]) + if (Key_State[RETROK_RIGHT]) return (!UI_is_active && Atari800_f_keys ? AKEY_F4 : (INPUT_key_shift ? AKEY_ASTERISK : AKEY_RIGHT)) ^ shiftctrl; - if (Key_Sate[RETROK_UP]) + if (Key_State[RETROK_UP]) return (!UI_is_active && Atari800_f_keys ? AKEY_F1 : (INPUT_key_shift ? AKEY_MINUS : AKEY_UP)) ^ shiftctrl; - if (Key_Sate[RETROK_DOWN]) + if (Key_State[RETROK_DOWN]) return (!UI_is_active && Atari800_f_keys ? AKEY_F2 : (INPUT_key_shift ? AKEY_EQUAL : AKEY_DOWN)) ^ shiftctrl; - if (Key_Sate[RETROK_ESCAPE]) + if (Key_State[RETROK_ESCAPE]) return AKEY_ESCAPE ^ shiftctrl; - if (Key_Sate[RETROK_TAB]) + if (Key_State[RETROK_TAB]) return AKEY_TAB ^ shiftctrl; - if (Key_Sate[RETROK_DELETE]){ + if (Key_State[RETROK_DELETE]){ if (INPUT_key_shift) return AKEY_DELETE_LINE|shiftctrl; else return AKEY_DELETE_CHAR; } - if (Key_Sate[RETROK_INSERT]){ + if (Key_State[RETROK_INSERT]){ if (INPUT_key_shift) return AKEY_INSERT_LINE|shiftctrl; else @@ -358,296 +428,315 @@ int PLATFORM_Keyboard(void) } if (INPUT_cx85){ - - if (Key_Sate[RETROK_KP1]) - return AKEY_CX85_1; - else if (Key_Sate[RETROK_KP2]) - return AKEY_CX85_2; - else if (Key_Sate[RETROK_KP2]) - return AKEY_CX85_3; - else if (Key_Sate[RETROK_KP3]) - return AKEY_CX85_4; - else if (Key_Sate[RETROK_KP4]) - return AKEY_CX85_5; - else if (Key_Sate[RETROK_KP5]) - return AKEY_CX85_6; - else if (Key_Sate[RETROK_KP6]) - return AKEY_CX85_7; - else if (Key_Sate[RETROK_KP7]) - return AKEY_CX85_8; - else if (Key_Sate[RETROK_KP8]) - return AKEY_CX85_9; - else if (Key_Sate[RETROK_KP9]) - return AKEY_CX85_0; - else if (Key_Sate[RETROK_KP0]) - return AKEY_CX85_2; - else if (Key_Sate[RETROK_KP_PERIOD]) - return AKEY_CX85_PERIOD; - else if (Key_Sate[RETROK_KP_MINUS]) - return AKEY_CX85_MINUS; - else if (Key_Sate[RETROK_KP_ENTER]) - return AKEY_CX85_PLUS_ENTER; - else if (Key_Sate[RETROK_KP_DIVIDE]) - return (key_control ? AKEY_CX85_ESCAPE : AKEY_CX85_NO); - else if (Key_Sate[RETROK_KP_MULTIPLY]) - return AKEY_CX85_DELETE; - else if (Key_Sate[RETROK_KP_PLUS]) - return AKEY_CX85_YES; + if (Key_State[RETROK_KP1]) return AKEY_CX85_1; + else if (Key_State[RETROK_KP2]) return AKEY_CX85_2; + else if (Key_State[RETROK_KP2]) return AKEY_CX85_3; + else if (Key_State[RETROK_KP3]) return AKEY_CX85_4; + else if (Key_State[RETROK_KP4]) return AKEY_CX85_5; + else if (Key_State[RETROK_KP5]) return AKEY_CX85_6; + else if (Key_State[RETROK_KP6]) return AKEY_CX85_7; + else if (Key_State[RETROK_KP7]) return AKEY_CX85_8; + else if (Key_State[RETROK_KP8]) return AKEY_CX85_9; + else if (Key_State[RETROK_KP9]) return AKEY_CX85_0; + else if (Key_State[RETROK_KP0]) return AKEY_CX85_2; + else if (Key_State[RETROK_KP_PERIOD]) return AKEY_CX85_PERIOD; + else if (Key_State[RETROK_KP_MINUS]) return AKEY_CX85_MINUS; + else if (Key_State[RETROK_KP_ENTER]) return AKEY_CX85_PLUS_ENTER; + else if (Key_State[RETROK_KP_DIVIDE]) return (key_control ? AKEY_CX85_ESCAPE : AKEY_CX85_NO); + else if (Key_State[RETROK_KP_MULTIPLY]) return AKEY_CX85_DELETE; + else if (Key_State[RETROK_KP_PLUS]) return AKEY_CX85_YES; } /* Handle CTRL-0 to CTRL-9 and other control characters */ if (key_control) { - if (Key_Sate[RETROK_PERIOD]) - return AKEY_FULLSTOP|shiftctrl; - if (Key_Sate[RETROK_COMMA]) - return AKEY_COMMA|shiftctrl; - if (Key_Sate[RETROK_SEMICOLON]) - return AKEY_SEMICOLON|shiftctrl; - if (Key_Sate[RETROK_SLASH]) - return AKEY_SLASH|shiftctrl; - if (Key_Sate[RETROK_BACKSLASH]) - return AKEY_ESCAPE|shiftctrl; - if (Key_Sate[RETROK_0]) - return AKEY_CTRL_0|shiftctrl; - if (Key_Sate[RETROK_1]) - return AKEY_CTRL_1|shiftctrl; - if (Key_Sate[RETROK_2]) - return AKEY_CTRL_2|shiftctrl; - if (Key_Sate[RETROK_3]) - return AKEY_CTRL_3|shiftctrl; - if (Key_Sate[RETROK_4]) - return AKEY_CTRL_4|shiftctrl; - if (Key_Sate[RETROK_5]) - return AKEY_CTRL_5|shiftctrl; - if (Key_Sate[RETROK_6]) - return AKEY_CTRL_6|shiftctrl; - if (Key_Sate[RETROK_7]) - return AKEY_CTRL_7|shiftctrl; - if (Key_Sate[RETROK_8]) - return AKEY_CTRL_8|shiftctrl; - if (Key_Sate[RETROK_9]) - return AKEY_CTRL_9|shiftctrl; - - if (Key_Sate[RETROK_a])return AKEY_CTRL_a; - if (Key_Sate[RETROK_b])return AKEY_CTRL_b; - if (Key_Sate[RETROK_c])return AKEY_CTRL_c; - if (Key_Sate[RETROK_d])return AKEY_CTRL_d; - if (Key_Sate[RETROK_e])return AKEY_CTRL_e; - if (Key_Sate[RETROK_f])return AKEY_CTRL_f; - if (Key_Sate[RETROK_g])return AKEY_CTRL_g; - if (Key_Sate[RETROK_h])return AKEY_CTRL_h; - if (Key_Sate[RETROK_i])return AKEY_CTRL_i; - if (Key_Sate[RETROK_j])return AKEY_CTRL_j; - if (Key_Sate[RETROK_k])return AKEY_CTRL_k; - if (Key_Sate[RETROK_l])return AKEY_CTRL_l; - if (Key_Sate[RETROK_m])return AKEY_CTRL_m; - if (Key_Sate[RETROK_n])return AKEY_CTRL_n; - if (Key_Sate[RETROK_o])return AKEY_CTRL_o; - if (Key_Sate[RETROK_p])return AKEY_CTRL_p; - if (Key_Sate[RETROK_q])return AKEY_CTRL_q; - if (Key_Sate[RETROK_r])return AKEY_CTRL_r; - if (Key_Sate[RETROK_s])return AKEY_CTRL_s; - if (Key_Sate[RETROK_t])return AKEY_CTRL_t; - if (Key_Sate[RETROK_u])return AKEY_CTRL_u; - if (Key_Sate[RETROK_v])return AKEY_CTRL_v; - if (Key_Sate[RETROK_w])return AKEY_CTRL_w; - if (Key_Sate[RETROK_x])return AKEY_CTRL_x; - if (Key_Sate[RETROK_y])return AKEY_CTRL_y; - if (Key_Sate[RETROK_z])return AKEY_CTRL_z; + if (Key_State[RETROK_PERIOD]) return AKEY_FULLSTOP|shiftctrl; + if (Key_State[RETROK_COMMA]) return AKEY_COMMA|shiftctrl; + if (Key_State[RETROK_SEMICOLON]) return AKEY_SEMICOLON|shiftctrl; + if (Key_State[RETROK_SLASH]) return AKEY_SLASH|shiftctrl; + if (Key_State[RETROK_BACKSLASH]) return AKEY_ESCAPE|shiftctrl; + if (Key_State[RETROK_0]) return AKEY_CTRL_0|shiftctrl; + if (Key_State[RETROK_1]) return AKEY_CTRL_1|shiftctrl; + if (Key_State[RETROK_2]) return AKEY_CTRL_2|shiftctrl; + if (Key_State[RETROK_3]) return AKEY_CTRL_3|shiftctrl; + if (Key_State[RETROK_4]) return AKEY_CTRL_4|shiftctrl; + if (Key_State[RETROK_5]) return AKEY_CTRL_5|shiftctrl; + if (Key_State[RETROK_6]) return AKEY_CTRL_6|shiftctrl; + if (Key_State[RETROK_7]) return AKEY_CTRL_7|shiftctrl; + if (Key_State[RETROK_8]) return AKEY_CTRL_8|shiftctrl; + if (Key_State[RETROK_9]) return AKEY_CTRL_9|shiftctrl; + + if (Key_State[RETROK_a]) return AKEY_CTRL_a; + if (Key_State[RETROK_b]) return AKEY_CTRL_b; + if (Key_State[RETROK_c]) return AKEY_CTRL_c; + if (Key_State[RETROK_d]) return AKEY_CTRL_d; + if (Key_State[RETROK_e]) return AKEY_CTRL_e; + if (Key_State[RETROK_f]) return AKEY_CTRL_f; + if (Key_State[RETROK_g]) return AKEY_CTRL_g; + if (Key_State[RETROK_h]) return AKEY_CTRL_h; + if (Key_State[RETROK_i]) return AKEY_CTRL_i; + if (Key_State[RETROK_j]) return AKEY_CTRL_j; + if (Key_State[RETROK_k]) return AKEY_CTRL_k; + if (Key_State[RETROK_l]) return AKEY_CTRL_l; + if (Key_State[RETROK_m]) return AKEY_CTRL_m; + if (Key_State[RETROK_n]) return AKEY_CTRL_n; + if (Key_State[RETROK_o]) return AKEY_CTRL_o; + if (Key_State[RETROK_p]) return AKEY_CTRL_p; + if (Key_State[RETROK_q]) return AKEY_CTRL_q; + if (Key_State[RETROK_r]) return AKEY_CTRL_r; + if (Key_State[RETROK_s]) return AKEY_CTRL_s; + if (Key_State[RETROK_t]) return AKEY_CTRL_t; + if (Key_State[RETROK_u]) return AKEY_CTRL_u; + if (Key_State[RETROK_v]) return AKEY_CTRL_v; + if (Key_State[RETROK_w]) return AKEY_CTRL_w; + if (Key_State[RETROK_x]) return AKEY_CTRL_x; + if (Key_State[RETROK_y]) return AKEY_CTRL_y; + if (Key_State[RETROK_z]) return AKEY_CTRL_z; /* these three keys also type control-graphics characters, but there don't seem to be AKEY_ values for them! */ - if (Key_Sate[RETROK_COMMA])return (AKEY_CTRL | AKEY_COMMA); - if (Key_Sate[RETROK_PERIOD])return (AKEY_CTRL | AKEY_FULLSTOP); - if (Key_Sate[RETROK_SEMICOLON])return (AKEY_CTRL | AKEY_SEMICOLON); + if (Key_State[RETROK_COMMA]) return (AKEY_CTRL | AKEY_COMMA); + if (Key_State[RETROK_PERIOD]) return (AKEY_CTRL | AKEY_FULLSTOP); + if (Key_State[RETROK_SEMICOLON]) return (AKEY_CTRL | AKEY_SEMICOLON); - if (Key_Sate[RETROK_F7])return (AKEY_CTRL | AKEY_F1); - if (Key_Sate[RETROK_F8])return (AKEY_CTRL | AKEY_F2); - if (Key_Sate[RETROK_F9])return (AKEY_CTRL | AKEY_F3); - if (Key_Sate[RETROK_F10])return (AKEY_CTRL | AKEY_F4); + if (Key_State[RETROK_F7]) return (AKEY_CTRL | AKEY_F1); + if (Key_State[RETROK_F8]) return (AKEY_CTRL | AKEY_F2); + if (Key_State[RETROK_F9]) return (AKEY_CTRL | AKEY_F3); + if (Key_State[RETROK_F10]) return (AKEY_CTRL | AKEY_F4); // cursor keys - if (Key_Sate[RETROK_PLUS])return AKEY_LEFT; - if (Key_Sate[RETROK_ASTERISK])return AKEY_RIGHT; - if (Key_Sate[RETROK_EQUALS])return AKEY_DOWN; - if (Key_Sate[RETROK_UNDERSCORE])return AKEY_UP; - - + if (Key_State[RETROK_PLUS]) return AKEY_LEFT; + if (Key_State[RETROK_ASTERISK]) return AKEY_RIGHT; + if (Key_State[RETROK_EQUALS]) return AKEY_DOWN; + if (Key_State[RETROK_UNDERSCORE]) return AKEY_UP; } /* handle all keys */ if (INPUT_key_shift) { - if (Key_Sate[RETROK_a])return AKEY_A; - if (Key_Sate[RETROK_b])return AKEY_B; - if (Key_Sate[RETROK_c])return AKEY_C; - if (Key_Sate[RETROK_d])return AKEY_D; - if (Key_Sate[RETROK_e])return AKEY_E; - if (Key_Sate[RETROK_f])return AKEY_F; - if (Key_Sate[RETROK_g])return AKEY_G; - if (Key_Sate[RETROK_h])return AKEY_H; - if (Key_Sate[RETROK_i])return AKEY_I; - if (Key_Sate[RETROK_j])return AKEY_J; - if (Key_Sate[RETROK_k])return AKEY_K; - if (Key_Sate[RETROK_l])return AKEY_L; - if (Key_Sate[RETROK_m])return AKEY_M; - if (Key_Sate[RETROK_n])return AKEY_N; - if (Key_Sate[RETROK_o])return AKEY_O; - if (Key_Sate[RETROK_p])return AKEY_P; - if (Key_Sate[RETROK_q])return AKEY_Q; - if (Key_Sate[RETROK_r])return AKEY_R; - if (Key_Sate[RETROK_s])return AKEY_S; - if (Key_Sate[RETROK_t])return AKEY_T; - if (Key_Sate[RETROK_u])return AKEY_U; - if (Key_Sate[RETROK_v])return AKEY_V; - if (Key_Sate[RETROK_w])return AKEY_W; - if (Key_Sate[RETROK_x])return AKEY_X; - if (Key_Sate[RETROK_y])return AKEY_Y; - if (Key_Sate[RETROK_z])return AKEY_Z; - - if (Key_Sate[RETROK_1])return AKEY_EXCLAMATION; - if (Key_Sate[RETROK_2])return AKEY_DBLQUOTE; - if (Key_Sate[RETROK_3])return AKEY_HASH; - if (Key_Sate[RETROK_4])return AKEY_DOLLAR; - if (Key_Sate[RETROK_5])return AKEY_PERCENT; - if (Key_Sate[RETROK_6])return AKEY_AMPERSAND; - if (Key_Sate[RETROK_7])return AKEY_QUOTE; - if (Key_Sate[RETROK_8])return AKEY_AT; - if (Key_Sate[RETROK_9])return AKEY_PARENLEFT; - if (Key_Sate[RETROK_0])return AKEY_PARENRIGHT; - - if (Key_Sate[RETROK_BACKSLASH])return AKEY_BAR; - if (Key_Sate[RETROK_COMMA])return AKEY_BRACKETLEFT; - if (Key_Sate[RETROK_PERIOD])return AKEY_BRACKETRIGHT; - if (Key_Sate[RETROK_UNDERSCORE])return AKEY_MINUS; - if (Key_Sate[RETROK_PLUS])return AKEY_BACKSLASH; - if (Key_Sate[RETROK_EQUALS])return AKEY_BAR; - if (Key_Sate[RETROK_LEFTBRACKET])return AKEY_BRACKETLEFT; // no curly braces on Atari - if (Key_Sate[RETROK_RIGHTBRACKET])return AKEY_BRACKETRIGHT; // no curly braces on Atari - if (Key_Sate[RETROK_SEMICOLON])return AKEY_COLON; - if (Key_Sate[RETROK_QUOTE])return AKEY_DBLQUOTE; - if (Key_Sate[RETROK_SLASH])return AKEY_QUESTION; - if (Key_Sate[RETROK_ASTERISK])return AKEY_CIRCUMFLEX; - if (Key_Sate[RETROK_LESS])return AKEY_LESS; - if (Key_Sate[RETROK_GREATER])return AKEY_GREATER; + if (Key_State[RETROK_a]) return AKEY_A; + if (Key_State[RETROK_b]) return AKEY_B; + if (Key_State[RETROK_c]) return AKEY_C; + if (Key_State[RETROK_d]) return AKEY_D; + if (Key_State[RETROK_e]) return AKEY_E; + if (Key_State[RETROK_f]) return AKEY_F; + if (Key_State[RETROK_g]) return AKEY_G; + if (Key_State[RETROK_h]) return AKEY_H; + if (Key_State[RETROK_i]) return AKEY_I; + if (Key_State[RETROK_j]) return AKEY_J; + if (Key_State[RETROK_k]) return AKEY_K; + if (Key_State[RETROK_l]) return AKEY_L; + if (Key_State[RETROK_m]) return AKEY_M; + if (Key_State[RETROK_n]) return AKEY_N; + if (Key_State[RETROK_o]) return AKEY_O; + if (Key_State[RETROK_p]) return AKEY_P; + if (Key_State[RETROK_q]) return AKEY_Q; + if (Key_State[RETROK_r]) return AKEY_R; + if (Key_State[RETROK_s]) return AKEY_S; + if (Key_State[RETROK_t]) return AKEY_T; + if (Key_State[RETROK_u]) return AKEY_U; + if (Key_State[RETROK_v]) return AKEY_V; + if (Key_State[RETROK_w]) return AKEY_W; + if (Key_State[RETROK_x]) return AKEY_X; + if (Key_State[RETROK_y]) return AKEY_Y; + if (Key_State[RETROK_z]) return AKEY_Z; + + if (Key_State[RETROK_1]) return AKEY_EXCLAMATION; + if (Key_State[RETROK_2]) return AKEY_DBLQUOTE; + if (Key_State[RETROK_3]) return AKEY_HASH; + if (Key_State[RETROK_4]) return AKEY_DOLLAR; + if (Key_State[RETROK_5]) return AKEY_PERCENT; + if (Key_State[RETROK_6]) return AKEY_AMPERSAND; + if (Key_State[RETROK_7]) return AKEY_QUOTE; + if (Key_State[RETROK_8]) return AKEY_AT; + if (Key_State[RETROK_9]) return AKEY_PARENLEFT; + if (Key_State[RETROK_0]) return AKEY_PARENRIGHT; + + if (Key_State[RETROK_BACKSLASH]) return AKEY_BAR; + if (Key_State[RETROK_COMMA]) return AKEY_BRACKETLEFT; + if (Key_State[RETROK_PERIOD]) return AKEY_BRACKETRIGHT; + if (Key_State[RETROK_UNDERSCORE]) return AKEY_MINUS; + if (Key_State[RETROK_PLUS]) return AKEY_BACKSLASH; + if (Key_State[RETROK_EQUALS]) return AKEY_BAR; + if (Key_State[RETROK_LEFTBRACKET]) return AKEY_BRACKETLEFT; // no curly braces on Atari + if (Key_State[RETROK_RIGHTBRACKET]) return AKEY_BRACKETRIGHT; // no curly braces on Atari + if (Key_State[RETROK_SEMICOLON]) return AKEY_COLON; + if (Key_State[RETROK_QUOTE]) return AKEY_DBLQUOTE; + if (Key_State[RETROK_SLASH]) return AKEY_QUESTION; + if (Key_State[RETROK_ASTERISK]) return AKEY_CIRCUMFLEX; + if (Key_State[RETROK_LESS]) return AKEY_LESS; + if (Key_State[RETROK_GREATER]) return AKEY_GREATER; - if (Key_Sate[RETROK_F7])return (AKEY_SHFT | AKEY_F1); - if (Key_Sate[RETROK_F8])return (AKEY_SHFT | AKEY_F2); - if (Key_Sate[RETROK_F9])return (AKEY_SHFT | AKEY_F3); - if (Key_Sate[RETROK_F10])return (AKEY_SHFT | AKEY_F4); + if (Key_State[RETROK_F7]) return (AKEY_SHFT | AKEY_F1); + if (Key_State[RETROK_F8]) return (AKEY_SHFT | AKEY_F2); + if (Key_State[RETROK_F9]) return (AKEY_SHFT | AKEY_F3); + if (Key_State[RETROK_F10]) return (AKEY_SHFT | AKEY_F4); } else { - if (Key_Sate[RETROK_a])return AKEY_a; - if (Key_Sate[RETROK_b])return AKEY_b; - if (Key_Sate[RETROK_c])return AKEY_c; - if (Key_Sate[RETROK_d])return AKEY_d; - if (Key_Sate[RETROK_e])return AKEY_e; - if (Key_Sate[RETROK_f])return AKEY_f; - if (Key_Sate[RETROK_g])return AKEY_g; - if (Key_Sate[RETROK_h])return AKEY_h; - if (Key_Sate[RETROK_i])return AKEY_i; - if (Key_Sate[RETROK_j])return AKEY_j; - if (Key_Sate[RETROK_k])return AKEY_k; - if (Key_Sate[RETROK_l])return AKEY_l; - if (Key_Sate[RETROK_m])return AKEY_m; - if (Key_Sate[RETROK_n])return AKEY_n; - if (Key_Sate[RETROK_o])return AKEY_o; - if (Key_Sate[RETROK_p])return AKEY_p; - if (Key_Sate[RETROK_q])return AKEY_q; - if (Key_Sate[RETROK_r])return AKEY_r; - if (Key_Sate[RETROK_s])return AKEY_s; - if (Key_Sate[RETROK_t])return AKEY_t; - if (Key_Sate[RETROK_u])return AKEY_u; - if (Key_Sate[RETROK_v])return AKEY_v; - if (Key_Sate[RETROK_w])return AKEY_w; - if (Key_Sate[RETROK_x])return AKEY_x; - if (Key_Sate[RETROK_y])return AKEY_y; - if (Key_Sate[RETROK_z])return AKEY_z; - - if (Key_Sate[RETROK_0])return AKEY_0; - if (Key_Sate[RETROK_1])return AKEY_1; - if (Key_Sate[RETROK_2])return AKEY_2; - if (Key_Sate[RETROK_3])return AKEY_3; - if (Key_Sate[RETROK_4])return AKEY_4; - if (Key_Sate[RETROK_5])return AKEY_5; - if (Key_Sate[RETROK_6])return AKEY_6; - if (Key_Sate[RETROK_7])return AKEY_7; - if (Key_Sate[RETROK_8])return AKEY_8; - if (Key_Sate[RETROK_9])return AKEY_9; - - if (Key_Sate[RETROK_BACKSLASH])return AKEY_BACKSLASH; - if (Key_Sate[RETROK_COMMA])return AKEY_COMMA; - if (Key_Sate[RETROK_PERIOD])return AKEY_FULLSTOP; - if (Key_Sate[RETROK_MINUS])return AKEY_MINUS; - if (Key_Sate[RETROK_PLUS])return AKEY_PLUS; - if (Key_Sate[RETROK_EQUALS])return AKEY_EQUAL; - if (Key_Sate[RETROK_LEFTBRACKET])return AKEY_BRACKETLEFT; - if (Key_Sate[RETROK_RIGHTBRACKET])return AKEY_BRACKETRIGHT; - if (Key_Sate[RETROK_SEMICOLON])return AKEY_SEMICOLON; - if (Key_Sate[RETROK_QUOTE])return AKEY_QUOTE; - if (Key_Sate[RETROK_SLASH])return AKEY_SLASH; - if (Key_Sate[RETROK_ASTERISK])return AKEY_ASTERISK; - if (Key_Sate[RETROK_LESS])return AKEY_LESS; - if (Key_Sate[RETROK_GREATER])return AKEY_GREATER; - if (Key_Sate[RETROK_UNDERSCORE])return AKEY_UNDERSCORE; + if (Key_State[RETROK_a]) return AKEY_a; + if (Key_State[RETROK_b]) return AKEY_b; + if (Key_State[RETROK_c]) return AKEY_c; + if (Key_State[RETROK_d]) return AKEY_d; + if (Key_State[RETROK_e]) return AKEY_e; + if (Key_State[RETROK_f]) return AKEY_f; + if (Key_State[RETROK_g]) return AKEY_g; + if (Key_State[RETROK_h]) return AKEY_h; + if (Key_State[RETROK_i]) return AKEY_i; + if (Key_State[RETROK_j]) return AKEY_j; + if (Key_State[RETROK_k]) return AKEY_k; + if (Key_State[RETROK_l]) return AKEY_l; + if (Key_State[RETROK_m]) return AKEY_m; + if (Key_State[RETROK_n]) return AKEY_n; + if (Key_State[RETROK_o]) return AKEY_o; + if (Key_State[RETROK_p]) return AKEY_p; + if (Key_State[RETROK_q]) return AKEY_q; + if (Key_State[RETROK_r]) return AKEY_r; + if (Key_State[RETROK_s]) return AKEY_s; + if (Key_State[RETROK_t]) return AKEY_t; + if (Key_State[RETROK_u]) return AKEY_u; + if (Key_State[RETROK_v]) return AKEY_v; + if (Key_State[RETROK_w]) return AKEY_w; + if (Key_State[RETROK_x]) return AKEY_x; + if (Key_State[RETROK_y]) return AKEY_y; + if (Key_State[RETROK_z]) return AKEY_z; + + if (Key_State[RETROK_0]) return AKEY_0; + if (Key_State[RETROK_1]) return AKEY_1; + if (Key_State[RETROK_2]) return AKEY_2; + if (Key_State[RETROK_3]) return AKEY_3; + if (Key_State[RETROK_4]) return AKEY_4; + if (Key_State[RETROK_5]) return AKEY_5; + if (Key_State[RETROK_6]) return AKEY_6; + if (Key_State[RETROK_7]) return AKEY_7; + if (Key_State[RETROK_8]) return AKEY_8; + if (Key_State[RETROK_9]) return AKEY_9; + + if (Key_State[RETROK_BACKSLASH]) return AKEY_BACKSLASH; + if (Key_State[RETROK_COMMA]) return AKEY_COMMA; + if (Key_State[RETROK_PERIOD]) return AKEY_FULLSTOP; + if (Key_State[RETROK_MINUS]) return AKEY_MINUS; + if (Key_State[RETROK_PLUS]) return AKEY_PLUS; + if (Key_State[RETROK_EQUALS]) return AKEY_EQUAL; + if (Key_State[RETROK_LEFTBRACKET]) return AKEY_BRACKETLEFT; + if (Key_State[RETROK_RIGHTBRACKET]) return AKEY_BRACKETRIGHT; + if (Key_State[RETROK_SEMICOLON]) return AKEY_SEMICOLON; + if (Key_State[RETROK_QUOTE]) return AKEY_QUOTE; + if (Key_State[RETROK_SLASH]) return AKEY_SLASH; + if (Key_State[RETROK_ASTERISK]) return AKEY_ASTERISK; + if (Key_State[RETROK_LESS]) return AKEY_LESS; + if (Key_State[RETROK_GREATER]) return AKEY_GREATER; + if (Key_State[RETROK_UNDERSCORE]) return AKEY_UNDERSCORE; - if (Key_Sate[RETROK_F7])return AKEY_F1; - if (Key_Sate[RETROK_F8])return AKEY_F2; - if (Key_Sate[RETROK_F9])return AKEY_F3; - if (Key_Sate[RETROK_F10])return AKEY_F4; - + if (Key_State[RETROK_F7]) return AKEY_F1; + if (Key_State[RETROK_F8]) return AKEY_F2; + if (Key_State[RETROK_F9]) return AKEY_F3; + if (Key_State[RETROK_F10]) return AKEY_F4; } /* FIXME joy bind */ + if (!UI_is_active) + { + if (SHOWKEYDELAY) + SHOWKEYDELAY--; + + for (int i = 0; i < 4; i++) + { + if (SHOWKEY == -1) + { + if (mbt[i][RETRO_DEVICE_ID_JOYPAD_SELECT]) + INPUT_key_consol &= (~INPUT_CONSOL_SELECT); + if (mbt[i][RETRO_DEVICE_ID_JOYPAD_START]) + INPUT_key_consol &= (~INPUT_CONSOL_START); + if (mbt[i][RETRO_DEVICE_ID_JOYPAD_L]) + INPUT_key_consol &= (~INPUT_CONSOL_OPTION); + if (mbt[i][RETRO_DEVICE_ID_JOYPAD_R]) + return AKEY_UI; + if (mbt[i][RETRO_DEVICE_ID_JOYPAD_Y]) + return AKEY_SPACE; + if (mbt[i][RETRO_DEVICE_ID_JOYPAD_L2]) + return AKEY_ESCAPE; + if (mbt[i][RETRO_DEVICE_ID_JOYPAD_X]) + return AKEY_RETURN; + if (mbt[i][RETRO_DEVICE_ID_JOYPAD_R2]) + return AKEY_HELP; + } + + if (mbt[i][RETRO_DEVICE_ID_JOYPAD_L3]) + if (!SHOWKEYDELAY) + { + SHOWKEY = -SHOWKEY; + SHOWKEYDELAY = 20; + } + } - if (mbt[RETRO_DEVICE_ID_JOYPAD_SELECT]) - INPUT_key_consol &= (~INPUT_CONSOL_SELECT); - if (mbt[RETRO_DEVICE_ID_JOYPAD_START]) - INPUT_key_consol &= (~INPUT_CONSOL_START); - if (mbt[RETRO_DEVICE_ID_JOYPAD_L]) - INPUT_key_consol &= (~INPUT_CONSOL_OPTION); - if (mbt[RETRO_DEVICE_ID_JOYPAD_R]) - return AKEY_UI; - if (mbt[RETRO_DEVICE_ID_JOYPAD_L2]) - return AKEY_SPACE; - if (mbt[RETRO_DEVICE_ID_JOYPAD_R2]) - return AKEY_ESCAPE; - if (mbt[RETRO_DEVICE_ID_JOYPAD_B]) - return AKEY_RETURN; - if (mbt[RETRO_DEVICE_ID_JOYPAD_Y]) - return AKEY_HELP; -} + /* not quite working when controller Device Type is ATARI Keyboard.. FIXME!*/ + //if (atari_devices[0] == RETRO_DEVICE_ATARI_KEYBOARD && (Key_State[RETROK_F11])) + // if (!SHOWKEYDELAY) + // { + // SHOWKEY = -SHOWKEY; + // SHOWKEYDELAY = 20; + // Key_State[RETROK_F11] = 0; + // } + } + /* not a big fan of hard coding input.. but it helps in a pinch of one accidently unmaps the controls + and this code is only used when the UI is active */ if (UI_is_active){ - // whitout kbd in GUI - if (MXjoy[0]&0x04) + // whithout kbd in GUI + if (MXjoy[0] & 0x04) return AKEY_LEFT; - if (MXjoy[0]&0x08) + if (MXjoy[0] & 0x08) return AKEY_RIGHT; - if (MXjoy[0]&0x01) + if (MXjoy[0] & 0x01) return AKEY_UP; - if (MXjoy[0]&0x02) + if (MXjoy[0] & 0x02) return AKEY_DOWN; - if (MXjoy[0]&0x80) + if (MXjoy[0] & 0x10) return AKEY_RETURN; - if (MXjoy[0]&0x40) + if (MXjoy[0] & 0x80) return AKEY_ESCAPE; } + /* This is my alternate internal UI controls... mapped based on players controller mapping. I can't decide which is the best to use + Maybe give the player the option? */ + //if (UI_is_active) { + // for (int i = 0; i < 4; i++) + // { + // if (mbt[i][RETRO_DEVICE_ID_JOYPAD_LEFT]) + // return AKEY_LEFT; + // if (mbt[i][RETRO_DEVICE_ID_JOYPAD_RIGHT]) + // return AKEY_RIGHT; + // if (mbt[i][RETRO_DEVICE_ID_JOYPAD_UP]) + // return AKEY_UP; + // if (mbt[i][RETRO_DEVICE_ID_JOYPAD_DOWN]) + // return AKEY_DOWN; + + // if (atari_devices[i] == RETRO_DEVICE_ATARI_JOYSTICK) + // whichButton = RETRO_DEVICE_ID_JOYPAD_A; + // else if (atari_devices[i] == RETRO_DEVICE_ATARI_5200_JOYSTICK) + // whichButton = RETRO_DEVICE_ID_JOYPAD_B; + // if (mbt[i][whichButton]) + // return AKEY_RETURN; + + // if (atari_devices[i] == RETRO_DEVICE_ATARI_JOYSTICK) + // whichButton = RETRO_DEVICE_ID_JOYPAD_R2; + // else if (atari_devices[i] == RETRO_DEVICE_ATARI_5200_JOYSTICK) + // whichButton = RETRO_DEVICE_ID_JOYPAD_A; + // if (mbt[i][whichButton]) + // return AKEY_ESCAPE; + // } + //} + return AKEY_NONE; - } /* int PLATFORM_GetRawKey(void) { - input_poll_cb(); for(i=0;i<320;i++) - Key_Sate[i]=input_state_cb(0, RETRO_DEVICE_KEYBOARD, 0,i) ? 0x80: 0; - + Key_State[i]=input_state_cb(0, RETRO_DEVICE_KEYBOARD, 0,i) ? 0x80: 0; } */ @@ -701,45 +790,77 @@ static void get_platform_PORT(unsigned char *s0, unsigned char *s1, unsigned cha int stick0, stick1, stick2, stick3; stick0 = stick1 = stick2 = stick3 = INPUT_STICK_CENTRE; + /* only send trigger (joyleft) if paddle mode is active and trigger button pressed*/ + if (!UI_is_active && paddle_mode) + { + if (PLATFORM_kbd_joy_0_enabled) { + if ((MXjoy[0] & 0x80)) + stick0 &= INPUT_STICK_LEFT; + } + + if (PLATFORM_kbd_joy_1_enabled) { + if ((MXjoy[1] & 0x80)) + stick1 &= INPUT_STICK_LEFT; + } + + if (PLATFORM_kbd_joy_2_enabled) { + if ((MXjoy[2] & 0x80)) + stick2 &= INPUT_STICK_LEFT; + } + + if (PLATFORM_kbd_joy_3_enabled) { + if ((MXjoy[3] & 0x80)) + stick3 &= INPUT_STICK_LEFT; + } + + *s0 = stick0; + *s1 = stick1; + *s2 = stick2; + *s3 = stick3; + + /* trig is all we care about */ + return; + } + if (PLATFORM_kbd_joy_0_enabled) { - if (MXjoy[0]&0x04) + if (MXjoy[0] & 0x04) stick0 &= INPUT_STICK_LEFT; - if (MXjoy[0]&0x08) + if (MXjoy[0] & 0x08) stick0 &= INPUT_STICK_RIGHT; - if (MXjoy[0]&0x01) + if (MXjoy[0] & 0x01) stick0 &= INPUT_STICK_FORWARD; - if (MXjoy[0]&0x02) + if (MXjoy[0] & 0x02) stick0 &= INPUT_STICK_BACK; } if (PLATFORM_kbd_joy_1_enabled) { - if (MXjoy[1]&0x04) + if (MXjoy[1] & 0x04) stick1 &= INPUT_STICK_LEFT; - if (MXjoy[1]&0x08) + if (MXjoy[1] & 0x08) stick1 &= INPUT_STICK_RIGHT; - if (MXjoy[1]&0x01) + if (MXjoy[1] & 0x01) stick1 &= INPUT_STICK_FORWARD; - if (MXjoy[1]&0x02) + if (MXjoy[1] & 0x02) stick1 &= INPUT_STICK_BACK; } if (PLATFORM_kbd_joy_2_enabled) { - if (MXjoy[2]&0x04) + if (MXjoy[2] & 0x04) stick2 &= INPUT_STICK_LEFT; - if (MXjoy[2]&0x08) + if (MXjoy[2] & 0x08) stick2 &= INPUT_STICK_RIGHT; - if (MXjoy[2]&0x01) + if (MXjoy[2] & 0x01) stick2 &= INPUT_STICK_FORWARD; - if (MXjoy[2]&0x02) + if (MXjoy[2] & 0x02) stick2 &= INPUT_STICK_BACK; } if (PLATFORM_kbd_joy_3_enabled) { - if (MXjoy[3]&0x04) + if (MXjoy[3] & 0x04) stick3 &= INPUT_STICK_LEFT; - if (MXjoy[3]&0x08) + if (MXjoy[3] & 0x08) stick3 &= INPUT_STICK_RIGHT; - if (MXjoy[3]&0x01) + if (MXjoy[3] & 0x01) stick3 &= INPUT_STICK_FORWARD; - if (MXjoy[3]&0x02) + if (MXjoy[3] & 0x02) stick3 &= INPUT_STICK_BACK; } @@ -764,19 +885,19 @@ static void get_platform_TRIG(unsigned char *t0, unsigned char *t1, unsigned cha trig0 = trig1 = trig2 = trig3 = 1; if (PLATFORM_kbd_joy_0_enabled) { - trig0 = MXjoy[0]&0x80?0:1; + trig0 = MXjoy[0] & 0x80 ? 0:1; } if (PLATFORM_kbd_joy_1_enabled) { - trig1 = MXjoy[1]&0x80?0:1; + trig1 = MXjoy[1] & 0x80 ? 0:1; } if (PLATFORM_kbd_joy_2_enabled) { - trig2 = MXjoy[2]&0x80?0:1; + trig2 = MXjoy[2] & 0x80 ? 0:1; } if (PLATFORM_kbd_joy_3_enabled) { - trig3 = MXjoy[3]&0x80?0:1; + trig3 = MXjoy[3] & 0x80 ? 0:1; } if (swap_joysticks) { @@ -796,6 +917,9 @@ static void get_platform_TRIG(unsigned char *t0, unsigned char *t1, unsigned cha int PLATFORM_PORT(int num) { + if (SHOWKEY == 1 ) + return 0xff; + if (num == 0) { UBYTE a, b, c, d; get_platform_PORT(&a, &b, &c, &d); @@ -815,6 +939,10 @@ int PLATFORM_PORT(int num) int PLATFORM_TRIG(int num) { UBYTE a, b, c, d; + + if (SHOWKEY == 1) + return 0x01; + get_platform_TRIG(&a, &b, &c, &d); switch (num) { diff --git a/libretro/retro_disk_control.c b/libretro/retro_disk_control.c new file mode 100644 index 0000000..31b6d45 --- /dev/null +++ b/libretro/retro_disk_control.c @@ -0,0 +1,428 @@ +/* Copyright (C) 2018 + * + * Permission is hereby granted, free of charge, + * to any person obtaining a copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include +#include "libretro.h" +#include "retro_disk_control.h" +#include "retro_strings.h" +#include "retro_utils.h" +#include "file/file_path.h" + +#include +#include +#include +#include + +/*#include +#include +#include +#include +#include +#include */ + +static void fallback_log(enum retro_log_level level, const char* fmt, ...); +static retro_log_printf_t log_cb = fallback_log; + +static void fallback_log(enum retro_log_level level, const char* fmt, ...) +{ + va_list va; + + (void)level; + + va_start(va, fmt); + vfprintf(stderr, fmt, va); + va_end(va); +} + +#define COMMENT "#" +#define M3U_SPECIAL_COMMAND "#COMMAND:" + +// Return the directory name of filename 'filename'. +static char* dirname_int(const char* filename) +{ + if (filename == NULL) + return NULL; + + // Find last separator + char* right = find_last_slash(filename); + if (right) + return strleft(filename, right - filename); + + // Not found + return NULL; +} + +static char* m3u_search_file(const char* basedir, const char* dskName) +{ + // If basedir was provided + if (basedir != NULL && !path_is_absolute(dskName)) + { + // Join basedir and dskName + char* dskPath = path_join_dup(basedir, dskName); + + // Verify if this item is a relative filename (append it to the m3u path) + if (file_exists(dskPath)) + { + // Return + return dskPath; + } + free(dskPath); + } + + // Verify if this item is an absolute pathname (or the file is in working dir) + if (file_exists(dskName)) + { + // Copy and return + return strdup(dskName); + } + + // File not found + return NULL; +} + +void dc_reset(dc_storage* dc) +{ + unsigned i; + + // Verify + if (dc == NULL) + return; + + // Clean the command + if (dc->command) + { + free(dc->command); + dc->command = NULL; + } + + // Clean the struct + for (i = 0; i < dc->count; i++) + { + free(dc->files[i]); + dc->files[i] = NULL; + free(dc->names[i]); + dc->names[i] = NULL; + + dc->types[i] = DC_IMAGE_TYPE_NONE; + } + + dc->unit = DC_IMAGE_TYPE_NONE; + dc->count = 0; + dc->index = 0; + dc->index_prev = 0; + dc->eject_state = true; + dc->replace = false; +} + +dc_storage* dc_create(void) +{ + int i; + + // Initialize the struct + dc_storage* dc = NULL; + + if ((dc = (dc_storage*)malloc(sizeof(dc_storage))) != NULL) + { + dc->unit = DC_IMAGE_TYPE_NONE; + dc->count = 0; + dc->index = 0; + dc->eject_state = true; + dc->replace = false; + dc->command = NULL; + for (i = 0; i < DC_MAX_SIZE; i++) + { + dc->files[i] = NULL; + dc->names[i] = NULL; + dc->types[i] = DC_IMAGE_TYPE_NONE; + } + } + + return dc; +} + +bool dc_add_file_int(dc_storage* dc, char* filename, char* name) +{ + /* Verify */ + if (dc == NULL) + return false; + + if (!filename || (*filename == '\0')) + return false; + + /* If max size is not exceeded */ + if (dc->count < DC_MAX_SIZE) + { + /* Add the file */ + dc->count++; + dc->files[dc->count - 1] = strdup(filename); + dc->names[dc->count - 1] = !string_is_empty(name) ? strdup(name) : NULL; + dc->types[dc->count - 1] = dc_get_image_type(filename); + + log_cb(RETRO_LOG_INFO, ">>> dc added int %s - [%s]\n", filename, name); + return true; + } + + return false; +} + +bool dc_add_file(dc_storage* dc, const char* filename) +{ + unsigned index = 0; + + /* Verify */ + if (dc == NULL || !filename || (*filename == '\0')) + return false; + + /* Dupecheck */ + for (index = 0; index < dc->count; index++) + { + if (!strcmp(dc->files[index], filename)) + { + log_cb(RETRO_LOG_INFO,"File '%s' ignored as duplicate!\n", filename); + return true; + } + } + + // Get 'name' - just the filename without extension + char name[512]; + name[0] = '\0'; + fill_pathname(name, path_basename(filename), "", sizeof(name)); + + if (!dc_add_file_int(dc, strdup(filename), strdup(name))) + return false; + + // if dc unit-type is none, get type from first image + if (dc->unit == DC_IMAGE_TYPE_NONE) + { + if (dc_get_image_type(dc->files[0]) == DC_IMAGE_TYPE_TAPE) + dc->unit = DC_IMAGE_TYPE_TAPE; + else if (dc_get_image_type(dc->files[0]) == DC_IMAGE_TYPE_FLOPPY) + dc->unit = DC_IMAGE_TYPE_FLOPPY; + else if (dc_get_image_type(dc->files[0]) == DC_IMAGE_TYPE_MEM) + dc->unit = DC_IMAGE_TYPE_MEM; + else + dc->unit = DC_IMAGE_TYPE_FLOPPY; + } + + log_cb(RETRO_LOG_INFO,">>> dc added %s - [%s] [unit %i]\n", filename, name, dc->unit); + return true; +} + +bool dc_remove_file(dc_storage* dc, int index) +{ + if (dc == NULL) + return false; + + if (index < 0 || index >= dc->count) + return false; + + // "If ptr is a null pointer, no action occurs" + free(dc->files[index]); + dc->files[index] = NULL; + free(dc->names[index]); + dc->names[index] = NULL; + dc->types[index] = DC_IMAGE_TYPE_NONE; + + // Shift all entries after index one slot up + if (index != dc->count - 1) + { + memmove(dc->files + index, dc->files + index + 1, (dc->count - 1 - index) * sizeof(dc->files[0])); + memmove(dc->names + index, dc->names + index + 1, (dc->count - 1 - index) * sizeof(dc->names[0])); + } + dc->count--; + + // Reset fliplist unit after removing last entry + if (dc->count == 0) + { + dc->unit = DC_IMAGE_TYPE_NONE; + } + + return true; +} + +int dc_replace_file(dc_storage* dc, int index, const char* filename) +{ + if (dc == NULL) + return false; + + if (index < 0 || index >= dc->count) + return false; + + // "If ptr is a null pointer, no action occurs" + free(dc->files[index]); + dc->files[index] = NULL; + free(dc->names[index]); + dc->names[index] = NULL; + dc->types[index] = DC_IMAGE_TYPE_NONE; + + if (filename == NULL) + { + dc_remove_file(dc, index); + } + else + { + dc->replace = false; + + char full_path_replace[RETRO_PATH_MAX] = { 0 }; + strncpy(full_path_replace, (char*)filename, sizeof(full_path_replace)); + + /* ZIP/M3U not implemented internally */ + if ( + strendswith(full_path_replace, "m3u") || + strendswith(full_path_replace, "zip") || + strendswith(full_path_replace, "7z") + ) + { + log_cb(RETRO_LOG_INFO,">>> dc replace %s unsupported type.\n", filename); + return false; + } + /* Single append */ + else + { + // Get 'name' - just the filename without extension + char name[512]; + name[0] = '\0'; + fill_pathname(name, path_basename(filename), "", sizeof(name)); + + /* Dupecheck */ + for (unsigned i = 0; i < dc->count - 1; i++) + { + if (!strcmp(dc->files[i], full_path_replace)) + { + dc_remove_file(dc, index); + return 2; // 2 = duplicate found + } + } + + dc->files[index] = strdup(filename); + dc->names[index] = !string_is_empty(name) ? strdup(name) : NULL; + dc->types[index] = dc_get_image_type(filename); + + log_cb(RETRO_LOG_INFO,">>> dc replace %s - %s [%u].\n", filename, name , dc->types[index] ); + } + } + + return true; +} + + +void dc_parse_m3u(dc_storage* dc, const char* m3u_file) +{ + // Verify + if (dc == NULL) + return; + + if (m3u_file == NULL) + return; + + FILE* fp = NULL; + + // Try to open the file + if ((fp = fopen(m3u_file, "r")) == NULL) + return; + + // Reset + dc_reset(dc); + + // Get the m3u base dir for resolving relative path + char* basedir = dirname_int(m3u_file); + + // Disk control interface 'name' for the following file + char* image_name = NULL; + + // Read the lines while there is line to read and we have enough space + char buffer[2048]; + while ((dc->count <= DC_MAX_SIZE) && (fgets(buffer, sizeof(buffer), fp) != NULL)) + { + char* string = trimwhitespace(buffer); + + // If it's a m3u special key or a file + if (strstartswith(string, M3U_SPECIAL_COMMAND)) + { + dc->command = strright(string, strlen(string) - strlen(M3U_SPECIAL_COMMAND)); + } + else if (!strstartswith(string, COMMENT)) + { + // Search the file (absolute, relative to m3u) + char* filename; + if ((filename = m3u_search_file(basedir, string)) != NULL) + { + + char tmp[512]; + tmp[0] = '\0'; + + fill_pathname(tmp, path_basename(filename), "", sizeof(tmp)); + image_name = strdup(tmp); + + // Add the file to the struct + dc_add_file_int(dc, filename, image_name); + image_name = NULL; + } + + } + } + + // If basedir was provided + if (basedir != NULL) + free(basedir); + + // Close the file + fclose(fp); + + if (dc->count != 0) + { + if (dc_get_image_type(dc->files[0]) == DC_IMAGE_TYPE_TAPE) + dc->unit = DC_IMAGE_TYPE_TAPE; + else if (dc_get_image_type(dc->files[0]) == DC_IMAGE_TYPE_FLOPPY) + dc->unit = DC_IMAGE_TYPE_FLOPPY; + else + dc->unit = DC_IMAGE_TYPE_FLOPPY; + + log_cb(RETRO_LOG_INFO,">>> dc (m3u) unit type: %i\n", dc->unit); + } +} + +void dc_free(dc_storage* dc) +{ + // Clean the struct + dc_reset(dc); + free(dc); + dc = NULL; + return; +} + +enum dc_image_type dc_get_image_type(const char* filename) +{ + // Missing file + if (!filename || (*filename == '\0')) + return DC_IMAGE_TYPE_NONE; + + // Floppy image + if (strendswith(filename, "atr") || + strendswith(filename, "atx") || + strendswith(filename, "xfd") || + strendswith(filename, "dcm")) + return DC_IMAGE_TYPE_FLOPPY; + + // Tape image. Removed "cdt" since it is not an Atari800 file type + if (strendswith(filename, "cas")) + return DC_IMAGE_TYPE_TAPE; + + // Fallback + return DC_IMAGE_TYPE_UNKNOWN; +} diff --git a/libretro/retro_disk_control.h b/libretro/retro_disk_control.h new file mode 100644 index 0000000..4033ca0 --- /dev/null +++ b/libretro/retro_disk_control.h @@ -0,0 +1,86 @@ +/**************************************************************************** + * Caprice32 libretro port + * + * Copyright not6 - r-type (2015-2018) + * Copyright David Colmenero - D_Skywalk (2019-2021) + * Copyright Daniel De Matteis (2012-2021) + * + * Redistribution and use of this code or any derivative works are permitted + * provided that the following conditions are met: + * + * - Redistributions may not be sold, nor may they be used in a commercial + * product or activity. + * + * - Redistributions that are modified from the original source must include the + * complete source code, including the source code for all components used by a + * binary built from the modified sources. However, as a special exception, the + * source code distributed need not include anything that is normally distributed + * (in either source or binary form) with the major components (compiler, kernel, + * and so on) of the operating system on which the executable runs, unless that + * component itself accompanies the executable. + * + * - Redistributions must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************************/ + +#ifndef RETRO_DISK_CONTROL_H__ +#define RETRO_DISK_CONTROL_H__ + +#include + +//***************************************************************************** +// Disk control structure and functions +#define DC_MAX_SIZE 20 + +//static INLINE bool string_is_empty(const char* data) +//{ +// return !data || (*data == '\0'); +//} + +enum dc_image_type { + DC_IMAGE_TYPE_NONE = 0, + DC_IMAGE_TYPE_FLOPPY, + DC_IMAGE_TYPE_TAPE, + DC_IMAGE_TYPE_MEM, + DC_IMAGE_TYPE_UNKNOWN +}; + +struct dc_storage{ + char* command; + char* files[DC_MAX_SIZE]; + char* names[DC_MAX_SIZE]; + enum dc_image_type types[DC_MAX_SIZE]; + unsigned unit; + unsigned count; + int index; + bool eject_state; + bool replace; + unsigned index_prev; +}; + +typedef struct dc_storage dc_storage; +dc_storage* dc_create(void); + +void dc_parse_m3u(dc_storage* dc, const char* m3u_file); +bool dc_add_file(dc_storage* dc, const char* filename); +void dc_free(dc_storage* dc); +void dc_reset(dc_storage* dc); +int dc_replace_file(dc_storage* dc, int index, const char* filename); +bool dc_remove_file(dc_storage* dc, int index); +enum dc_image_type dc_get_image_type(const char* filename); + +#endif diff --git a/libretro/retro_files.c b/libretro/retro_files.c new file mode 100644 index 0000000..6a94bee --- /dev/null +++ b/libretro/retro_files.c @@ -0,0 +1,28 @@ +/* Copyright (C) 2018 + * + * Permission is hereby granted, free of charge, + * to any person obtaining a copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include "retro_files.h" + +#include +#include +#include + +void path_join(char* out, const char* basedir, const char* filename) +{ + snprintf(out, RETRO_PATH_MAX, "%s%s%s", basedir, RETRO_PATH_SEPARATOR, filename); +} diff --git a/libretro/retro_files.h b/libretro/retro_files.h new file mode 100644 index 0000000..4a39f94 --- /dev/null +++ b/libretro/retro_files.h @@ -0,0 +1,38 @@ +/* Copyright (C) 2018 + * + * Permission is hereby granted, free of charge, + * to any person obtaining a copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef RETRO_FILES_H__ +#define RETRO_FILES_H__ + +#include + +//***************************************************************************** +// File helpers functions +#define RETRO_PATH_MAX 512 + +#ifdef _WIN32 +#define RETRO_PATH_SEPARATOR "\\" +// Windows also support the unix path separator +#define RETRO_PATH_SEPARATOR_ALT "/" +#else +#define RETRO_PATH_SEPARATOR "/" +#endif + +void path_join(char* out, const char* basedir, const char* filename); + +#endif diff --git a/libretro/retro_strings.c b/libretro/retro_strings.c new file mode 100644 index 0000000..5d8e5f0 --- /dev/null +++ b/libretro/retro_strings.c @@ -0,0 +1,86 @@ +/* Copyright (C) 2018 + * + * Permission is hereby granted, free of charge, + * to any person obtaining a copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include "retro_strings.h" + +#include +#include +#include +#include + +// Note: This function returns a pointer to a substring_left of the original string. +// If the given string was allocated dynamically, the caller must not overwrite +// that pointer with the returned value, since the original pointer must be +// deallocated using the same allocator with which it was allocated. The return +// value must NOT be deallocated using free() etc. +char* trimwhitespace(char *str) +{ + char *end; + + // Trim leading space + while(isspace((unsigned char)*str)) str++; + + if(*str == 0) // All spaces? + return str; + + // Trim trailing space + end = str + strlen(str) - 1; + while(end > str && isspace((unsigned char)*end)) end--; + + // Write new null terminator character + end[1] = '\0'; + + return str; +} + +// Returns a substring of 'str' that contains the 'len' leftmost characters of 'str'. +char* strleft(const char* str, int len) +{ + char* result = calloc(len + 1, sizeof(char)); + strncpy(result, str, len); + return result; +} + +// Returns a substring of 'str' that contains the 'len' rightmost characters of 'str'. +char* strright(const char* str, int len) +{ + int pos = strlen(str) - len; + char* result = calloc(len + 1, sizeof(char)); + strncpy(result, str + pos, len); + return result; +} + +// Returns true if 'str' starts with 'start' +bool strstartswith(const char* str, const char* start) +{ + if (strlen(str) >= strlen(start)) + if(!strncasecmp(str, start, strlen(start))) + return true; + + return false; +} + +// Returns true if 'str' ends with 'end' +bool strendswith(const char* str, const char* end) +{ + if (strlen(str) >= strlen(end)) + if(!strcasecmp((char*)&str[strlen(str)-strlen(end)], end)) + return true; + + return false; +} diff --git a/libretro/retro_strings.h b/libretro/retro_strings.h new file mode 100644 index 0000000..d4594fc --- /dev/null +++ b/libretro/retro_strings.h @@ -0,0 +1,32 @@ +/* Copyright (C) 2018 + * + * Permission is hereby granted, free of charge, + * to any person obtaining a copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef RETRO_STRINGS_H__ +#define RETRO_STRINGS_H__ + +#include + +//***************************************************************************** +// String helpers functions +char* trimwhitespace(char *str); +char* strleft(const char* str, int len); +char* strright(const char* str, int len); +bool strstartswith(const char* str, const char* start); +bool strendswith(const char* str, const char* end); + +#endif diff --git a/libretro/retro_utils.c b/libretro/retro_utils.c new file mode 100644 index 0000000..1348acf --- /dev/null +++ b/libretro/retro_utils.c @@ -0,0 +1,185 @@ +/**************************************************************************** + * Caprice32 libretro port + * + * Copyright not6 - r-type (2015-2018) + * Copyright David Colmenero - D_Skywalk (2019-2021) + * Copyright Daniel De Matteis (2012-2021) + * + * Redistribution and use of this code or any derivative works are permitted + * provided that the following conditions are met: + * + * - Redistributions may not be sold, nor may they be used in a commercial + * product or activity. + * + * - Redistributions that are modified from the original source must include the + * complete source code, including the source code for all components used by a + * binary built from the modified sources. However, as a special exception, the + * source code distributed need not include anything that is normally distributed + * (in either source or binary form) with the major components (compiler, kernel, + * and so on) of the operating system on which the executable runs, unless that + * component itself accompanies the executable. + * + * - Redistributions must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************************/ + + +#include +#include +#include +#include + +#include "retro_utils.h" +#ifdef VITA + #include "file/file_path.h" +#endif + +extern uint8_t* pbGPBuffer; + +// Verify file extension +bool file_check_extension(const char *filename, const size_t filename_size, const char *ext, const size_t ext_size) +{ +#ifndef __PS3__ + size_t file_len = strnlen(filename, filename_size); + size_t ext_len = strnlen(ext, ext_size); +#else + size_t file_len = strlen(filename) > filename_size ? filename_size : strlen(filename); + size_t ext_len = strlen(ext) > ext_size ? ext_size : strlen(ext); +#endif + + if( ext_len > file_len || file_len >= filename_size - 1) + return false; + + const char * file_ext = &filename[file_len - ext_len]; + + return (strncasecmp(file_ext, ext, filename_size) == 0); +} + +bool file_check_flag(const char *filename, const size_t filename_size, const char *flag, const size_t flag_size) +{ +#ifndef __PS3__ + size_t file_len = strnlen(filename, filename_size); + size_t flag_len = strnlen(flag, flag_size); +#else + size_t file_len = strlen(filename) > filename_size ? filename_size : strlen(filename); + size_t flag_len = strlen(flag) > flag_size ? flag_size : strlen(flag); +#endif + + for (int i = 0; i < file_len; i++) { + if (i + flag_len > file_len) + return false; + + if (strncasecmp(&filename[i], flag, flag_len) == 0) + return true; + } + return false; +} + +// Verify if file exists +bool file_exists(const char *filename) +{ +#ifdef VITA + if (path_is_valid(filename) && !path_is_directory(filename)) +#else + struct stat buf; + if (stat(filename, &buf) == 0 && + (buf.st_mode & (S_IRUSR|S_IWUSR)) && !(buf.st_mode & S_IFDIR)) +#endif + { + /* file points to user readable regular file */ + return true; + } + return false; +} + +int file_size (int file_num) +{ + struct stat s; + + if (!fstat(file_num, &s)) { + return s.st_size; + } else { + return 0; + } +} + +void path_join(char* out, const char* basedir, const char* filename) +{ + snprintf(out, RETRO_PATH_MAX, "%s%s%s", basedir, RETRO_PATH_SEPARATOR, filename); +} + +char* path_join_dup(const char* basedir, const char* filename) +{ + size_t dirlen = strlen(basedir); + size_t seplen = strlen(RETRO_PATH_SEPARATOR); + size_t filelen = strlen(filename); + char* result = (char*)malloc(dirlen + seplen + filelen + 1); + strcpy(result, basedir); + strcpy(result + dirlen, RETRO_PATH_SEPARATOR); + strcpy(result + dirlen + seplen, filename); + return result; +} + +/** + * D_Skywalk: Imported from my 3DS pituka implementation + * http://david.dantoine.org/proyecto/26/ + */ + +#ifdef _3DS +void* linearMemAlign(size_t size, size_t alignment); +void linearFree(void* mem); +#endif + +void *retro_malloc(size_t size) { + #ifdef _3DS + return linearMemAlign(size, 0x80); + #else + return malloc(size); + #endif +} + +void retro_free(void * mem) { + #ifdef _3DS + linearFree(mem); + #else + free(mem); + #endif +} + +// ----------------------------- crc32b -------------------------------- + +/* This is the basic CRC-32 calculation with some optimization but no +table lookup. The the byte reversal is avoided by shifting the crc reg +right instead of left and by using a reversed 32-bit word to represent +the polynomial. */ + +uint32_t crc32_calculate(uint8_t * data, uint32_t size) { + uint32_t byte, crc, mask; + + crc = 0xFFFFFFFF; + + for (int i = 0; i < size; i++) { + byte = data[i]; + crc = crc ^ byte; + for (int j = 7; j >= 0; j--) { + mask = -(crc & 1); + crc = (crc >> 1) ^ (0xedb88320 & mask); + } + } + return ~crc; +} + diff --git a/libretro/retro_utils.h b/libretro/retro_utils.h new file mode 100644 index 0000000..dd1f5d1 --- /dev/null +++ b/libretro/retro_utils.h @@ -0,0 +1,72 @@ +/**************************************************************************** + * Caprice32 libretro port + * + * Copyright not6 - r-type (2015-2018) + * Copyright David Colmenero - D_Skywalk (2019-2021) + * Copyright Daniel De Matteis (2012-2021) + * + * Redistribution and use of this code or any derivative works are permitted + * provided that the following conditions are met: + * + * - Redistributions may not be sold, nor may they be used in a commercial + * product or activity. + * + * - Redistributions that are modified from the original source must include the + * complete source code, including the source code for all components used by a + * binary built from the modified sources. However, as a special exception, the + * source code distributed need not include anything that is normally distributed + * (in either source or binary form) with the major components (compiler, kernel, + * and so on) of the operating system on which the executable runs, unless that + * component itself accompanies the executable. + * + * - Redistributions must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************************/ + + +#ifndef RETRO_UTILS_H__ +#define RETRO_UTILS_H__ + +#include +#include +#include + +//***************************************************************************** +// File helpers functions +#define RETRO_PATH_MAX 512 + +#ifdef _WIN32 +#define RETRO_PATH_SEPARATOR "\\" +// Windows also support the unix path separator +#define RETRO_PATH_SEPARATOR_ALT "/" +#else +#define RETRO_PATH_SEPARATOR "/" +#endif + +bool file_check_extension(const char *filename, const size_t filename_size, const char *ext, const size_t ext_size); +bool file_check_flag(const char *filename, const size_t filename_size, const char *flag, const size_t flag_size); +void path_join(char* out, const char* basedir, const char* filename); +char* path_join_dup(const char* basedir, const char* filename); + +bool file_exists(const char *filename); +int file_size (int file_num); +uint32_t get_hash(const char *filename); + +void *retro_malloc(size_t size); +void retro_free(void * mem); + +#endif