/* * cartridge.c - cartridge emulation * * Copyright (C) 2001-2010 Piotr Fusik * Copyright (C) 2001-2010 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 #include #include "atari.h" #include "binload.h" /* BINLOAD_loading_basic */ #include "cartridge.h" #include "memory.h" #ifdef IDE # include "ide.h" #endif #include "pia.h" #include "rtime.h" #include "util.h" #ifndef BASIC #include "statesav.h" #endif #ifdef AF80 #include "af80.h" #endif #ifdef BIT3 #include "bit3.h" #endif #include "log.h" /* #define DEBUG 1 */ int const CARTRIDGE_kb[CARTRIDGE_LAST_SUPPORTED + 1] = { 0, 8, /* CARTRIDGE_STD_8 */ 16, /* CARTRIDGE_STD_16 */ 16, /* CARTRIDGE_OSS_034M_16 */ 32, /* CARTRIDGE_5200_32 */ 32, /* CARTRIDGE_DB_32 */ 16, /* CARTRIDGE_5200_EE_16 */ 40, /* CARTRIDGE_5200_40 */ 64, /* CARTRIDGE_WILL_64 */ 64, /* CARTRIDGE_EXP_64 */ 64, /* CARTRIDGE_DIAMOND_64 */ 64, /* CARTRIDGE_SDX_64 */ 32, /* CARTRIDGE_XEGS_32 */ 64, /* CARTRIDGE_XEGS_64_07 */ 128, /* CARTRIDGE_XEGS_128 */ 16, /* CARTRIDGE_OSS_M091_16 */ 16, /* CARTRIDGE_5200_NS_16 */ 128, /* CARTRIDGE_ATRAX_128 */ 40, /* CARTRIDGE_BBSB_40 */ 8, /* CARTRIDGE_5200_8 */ 4, /* CARTRIDGE_5200_4 */ 8, /* CARTRIDGE_RIGHT_8 */ 32, /* CARTRIDGE_WILL_32 */ 256, /* CARTRIDGE_XEGS_256 */ 512, /* CARTRIDGE_XEGS_512 */ 1024, /* CARTRIDGE_XEGS_1024 */ 16, /* CARTRIDGE_MEGA_16 */ 32, /* CARTRIDGE_MEGA_32 */ 64, /* CARTRIDGE_MEGA_64 */ 128, /* CARTRIDGE_MEGA_128 */ 256, /* CARTRIDGE_MEGA_256 */ 512, /* CARTRIDGE_MEGA_512 */ 1024, /* CARTRIDGE_MEGA_1024 */ 32, /* CARTRIDGE_SWXEGS_32 */ 64, /* CARTRIDGE_SWXEGS_64 */ 128, /* CARTRIDGE_SWXEGS_128 */ 256, /* CARTRIDGE_SWXEGS_256 */ 512, /* CARTRIDGE_SWXEGS_512 */ 1024, /* CARTRIDGE_SWXEGS_1024 */ 8, /* CARTRIDGE_PHOENIX_8 */ 16, /* CARTRIDGE_BLIZZARD_16 */ 128, /* CARTRIDGE_ATMAX_128 */ 1024, /* CARTRIDGE_ATMAX_1024 */ 128, /* CARTRIDGE_SDX_128 */ 8, /* CARTRIDGE_OSS_8 */ 16, /* CARTRIDGE_OSS_043M_16 */ 4, /* CARTRIDGE_BLIZZARD_4 */ 32, /* CARTRIDGE_AST_32 */ 64, /* CARTRIDGE_ATRAX_SDX_64 */ 128, /* CARTRIDGE_ATRAX_SDX_128 */ 64, /* CARTRIDGE_TURBOSOFT_64 */ 128, /* CARTRIDGE_TURBOSOFT_128 */ 32, /* CARTRIDGE_ULTRACART_32 */ 8, /* CARTRIDGE_LOW_BANK_8 */ 128, /* CARTRIDGE_SIC_128 */ 256, /* CARTRIDGE_SIC_256 */ 512, /* CARTRIDGE_SIC_512 */ 2, /* CARTRIDGE_STD_2 */ 4, /* CARTRIDGE_STD_4 */ 4, /* CARTRIDGE_RIGHT_4 */ 32, /* CARTRIDGE_TURBO_HIT_32 */ 2048, /* CARTRIDGE_MEGA_2048 */ 128*1024, /* CARTRIDGE_THECART_128M */ 4096, /* CARTRIDGE_MEGA_4096 */ 2048, /* CARTRIDGE_MEGA_2048 */ 32*1024, /* CARTRIDGE_THECART_32M */ 64*1024, /* CARTRIDGE_THECART_64M */ 64 /* CARTRIDGE_XEGS_64_8F */ }; int CARTRIDGE_autoreboot = TRUE; static int CartIsFor5200(int type) { switch (type) { case CARTRIDGE_5200_32: case CARTRIDGE_5200_EE_16: case CARTRIDGE_5200_40: case CARTRIDGE_5200_NS_16: case CARTRIDGE_5200_8: case CARTRIDGE_5200_4: return TRUE; default: break; } return FALSE; } static int CartIsPassthrough(int type) { return type == CARTRIDGE_SDX_64 || type == CARTRIDGE_SDX_128 || type == CARTRIDGE_ATRAX_SDX_64 || type == CARTRIDGE_ATRAX_SDX_128; } CARTRIDGE_image_t CARTRIDGE_main = { CARTRIDGE_NONE, 0, 0, NULL, "" }; /* Left/Right cartridge */ CARTRIDGE_image_t CARTRIDGE_piggyback = { CARTRIDGE_NONE, 0, 0, NULL, "" }; /* Pass through cartridge for SpartaDOSX */ /* The currently active cartridge in the left slot - normally points to CARTRIDGE_main but can be switched to CARTRIDGE_piggyback if the main cartridge is a SpartaDOS X. */ static CARTRIDGE_image_t *active_cart = &CARTRIDGE_main; /* DB_32, XEGS_32, XEGS_07_64, XEGS_128, XEGS_256, XEGS_512, XEGS_1024, SWXEGS_32, SWXEGS_64, SWXEGS_128, SWXEGS_256, SWXEGS_512, SWXEGS_1024 */ static void set_bank_809F(int main, int old_state) { if (active_cart->state & 0x80) { MEMORY_Cart809fDisable(); MEMORY_CartA0bfDisable(); } else { MEMORY_Cart809fEnable(); MEMORY_CartA0bfEnable(); MEMORY_CopyROM(0x8000, 0x9fff, active_cart->image + active_cart->state * 0x2000); if (old_state & 0x80) MEMORY_CopyROM(0xa000, 0xbfff, active_cart->image + main); } } /* XEGS_8F_64 */ static void set_bank_XEGS_8F_64(void) { if (active_cart->state & 0x08) MEMORY_CopyROM(0x8000, 0x9fff, active_cart->image + (active_cart->state & ~0x08) * 0x2000); else /* $8000-$9FFF is left unconnected. */ MEMORY_dFillMem(0x8000, 0xff, 0x2000); } /* OSS_034M_16, OSS_043M_16, OSS_M091_16, OSS_8 */ static void set_bank_A0AF(int main, int old_state) { if (active_cart->state < 0) MEMORY_CartA0bfDisable(); else { MEMORY_CartA0bfEnable(); if (active_cart->state == 0xff) /* Fill cart area with 0xFF. */ MEMORY_dFillMem(0xa000, 0xff, 0x1000); else MEMORY_CopyROM(0xa000, 0xafff, active_cart->image + active_cart->state * 0x1000); if (old_state < 0) MEMORY_CopyROM(0xb000, 0xbfff, active_cart->image + main); } } /* WILL_64, EXP_64, DIAMOND_64, SDX_64, WILL_32, ATMAX_128, ATMAX_1024, ATRAX_128, ATRAX_SDX_64, TURBOSOFT_64, TURBOSOFT_128, ULTRACART_32, TURBO_HIT_32, THECART_128M, THECART_32M, THECART_64M */ static void set_bank_A0BF(int disable_mask, int bank_mask) { if (active_cart->state & disable_mask) MEMORY_CartA0bfDisable(); else { MEMORY_CartA0bfEnable(); MEMORY_CopyROM(0xa000, 0xbfff, active_cart->image + (active_cart->state & bank_mask) * 0x2000); } } /* MEGA_16, MEGA_32, MEGA_64, MEGA_128, MEGA_256, MEGA_512, MEGA_1024, MEGAMAX_2048, MEGA_2048 */ static void set_bank_80BF(void) { if (active_cart->state & 0x80) { MEMORY_Cart809fDisable(); MEMORY_CartA0bfDisable(); } else { MEMORY_Cart809fEnable(); MEMORY_CartA0bfEnable(); MEMORY_CopyROM(0x8000, 0xbfff, active_cart->image + (active_cart->state & 0x7f) * 0x4000); } } static void set_bank_SDX_128(void) { if (active_cart->state & 8) MEMORY_CartA0bfDisable(); else { MEMORY_CartA0bfEnable(); MEMORY_CopyROM(0xa000, 0xbfff, active_cart->image + ((active_cart->state & 7) + ((active_cart->state & 0x10) >> 1)) * 0x2000); } } static void set_bank_SIC(int n) { if (!(active_cart->state & 0x20)) MEMORY_Cart809fDisable(); else { MEMORY_Cart809fEnable(); MEMORY_CopyROM(0x8000, 0x9fff, active_cart->image + (active_cart->state & n) * 0x4000); } if (active_cart->state & 0x40) MEMORY_CartA0bfDisable(); else { MEMORY_CartA0bfEnable(); MEMORY_CopyROM(0xa000, 0xbfff, active_cart->image + (active_cart->state & n) * 0x4000 + 0x2000); } } /* MEGA_4096 */ static void set_bank_MEGA_4096(void) { if (active_cart->state == 0xff) { MEMORY_Cart809fDisable(); MEMORY_CartA0bfDisable(); } else { MEMORY_Cart809fEnable(); MEMORY_CartA0bfEnable(); MEMORY_CopyROM(0x8000, 0xbfff, active_cart->image + active_cart->state * 0x4000); } } /* Called on a read or write operation to page $D5. Switches banks or enables/disables the cartridge pointed to by *active_cart. */ static void SwitchBank(int old_state) { /* All bank-switched cartridges besides two BBSB's are included in this swithch. The BBSB cartridges are not bank-switched by access to page $D5, but in CARTRIDGE_BountyBob1() and CARTRIDGE_BountyBob2(), so they need not be processed here. */ switch (active_cart->type) { case CARTRIDGE_OSS_034M_16: case CARTRIDGE_OSS_043M_16: set_bank_A0AF(0x3000, old_state); break; case CARTRIDGE_OSS_M091_16: case CARTRIDGE_OSS_8: set_bank_A0AF(0x0000, old_state); break; case CARTRIDGE_WILL_64: case CARTRIDGE_EXP_64: case CARTRIDGE_DIAMOND_64: case CARTRIDGE_SDX_64: case CARTRIDGE_WILL_32: case CARTRIDGE_ATRAX_SDX_64: set_bank_A0BF(8, 7); break; case CARTRIDGE_DB_32: case CARTRIDGE_XEGS_32: case CARTRIDGE_SWXEGS_32: set_bank_809F(0x6000, old_state); break; case CARTRIDGE_XEGS_07_64: case CARTRIDGE_SWXEGS_64: set_bank_809F(0xe000, old_state); break; case CARTRIDGE_XEGS_8F_64: set_bank_XEGS_8F_64(); break; case CARTRIDGE_XEGS_128: case CARTRIDGE_SWXEGS_128: set_bank_809F(0x1e000, old_state); break; case CARTRIDGE_XEGS_256: case CARTRIDGE_SWXEGS_256: set_bank_809F(0x3e000, old_state); break; case CARTRIDGE_XEGS_512: case CARTRIDGE_SWXEGS_512: set_bank_809F(0x7e000, old_state); break; case CARTRIDGE_XEGS_1024: case CARTRIDGE_SWXEGS_1024: set_bank_809F(0xfe000, old_state); break; case CARTRIDGE_ATRAX_128: case CARTRIDGE_ATMAX_1024: set_bank_A0BF(0x80, 0x7f); break; case CARTRIDGE_ATMAX_128: case CARTRIDGE_TURBOSOFT_64: case CARTRIDGE_TURBOSOFT_128: set_bank_A0BF(0x10, 0x0f); break; case CARTRIDGE_MEGA_16: case CARTRIDGE_MEGA_32: case CARTRIDGE_MEGA_64: case CARTRIDGE_MEGA_128: case CARTRIDGE_MEGA_256: case CARTRIDGE_MEGA_512: case CARTRIDGE_MEGA_1024: case CARTRIDGE_MEGA_2048: case CARTRIDGE_MEGAMAX_2048: set_bank_80BF(); break; case CARTRIDGE_PHOENIX_8: case CARTRIDGE_BLIZZARD_4: if (active_cart->state) MEMORY_CartA0bfDisable(); break; case CARTRIDGE_BLIZZARD_16: if (active_cart->state) { MEMORY_Cart809fDisable(); MEMORY_CartA0bfDisable(); } break; case CARTRIDGE_SDX_128: case CARTRIDGE_ATRAX_SDX_128: set_bank_SDX_128(); break; case CARTRIDGE_AST_32: /* Value 0x10000 indicates cartridge enabled. */ if (active_cart->state < 0x10000) MEMORY_CartA0bfDisable(); break; case CARTRIDGE_ULTRACART_32: case CARTRIDGE_BLIZZARD_32: set_bank_A0BF(4, 3); break; case CARTRIDGE_SIC_128: set_bank_SIC(0x07); break; case CARTRIDGE_SIC_256: set_bank_SIC(0x0f); break; case CARTRIDGE_SIC_512: set_bank_SIC(0x1f); break; case CARTRIDGE_MEGA_4096: set_bank_MEGA_4096(); break; case CARTRIDGE_THECART_128M: set_bank_A0BF(0x4000, 0x3fff); break; case CARTRIDGE_THECART_32M: set_bank_A0BF(0x4000, 0x0fff); break; case CARTRIDGE_THECART_64M: set_bank_A0BF(0x4000, 0x1fff); break; } #if DEBUG if (old_state != active_cart->state) Log_print("Cart %i state: %02x -> %02x", active_cart == &CARTRIDGE_piggyback, old_state, active_cart->state); #endif } /* Maps *active_cart to memory. If the cartridge is bankswitched, the mapping is performed according to its current state (ie. it doesn't reset to bank 0 or whatever). */ /* Note that this function only maps part of a cartridge (if any). Then it calls SwitchBank(), which maps the rest. */ static void MapActiveCart(void) { if (Atari800_machine_type == Atari800_MACHINE_5200) { MEMORY_SetROM(0x4ff6, 0x4ff9); /* disable Bounty Bob bank switching */ MEMORY_SetROM(0x5ff6, 0x5ff9); switch (active_cart->type) { case CARTRIDGE_5200_32: MEMORY_CopyROM(0x4000, 0xbfff, active_cart->image); break; case CARTRIDGE_5200_EE_16: MEMORY_CopyROM(0x4000, 0x5fff, active_cart->image); MEMORY_CopyROM(0x6000, 0x9fff, active_cart->image); MEMORY_CopyROM(0xa000, 0xbfff, active_cart->image + 0x2000); break; case CARTRIDGE_5200_40: MEMORY_CopyROM(0x4000, 0x4fff, active_cart->image + (active_cart->state & 0x03) * 0x1000); MEMORY_CopyROM(0x5000, 0x5fff, active_cart->image + 0x4000 + ((active_cart->state & 0x0c) >> 2) * 0x1000); MEMORY_CopyROM(0x8000, 0x9fff, active_cart->image + 0x8000); MEMORY_CopyROM(0xa000, 0xbfff, active_cart->image + 0x8000); #ifndef PAGED_ATTRIB MEMORY_SetHARDWARE(0x4ff6, 0x4ff9); MEMORY_SetHARDWARE(0x5ff6, 0x5ff9); #else MEMORY_readmap[0x4f] = CARTRIDGE_BountyBob1GetByte; MEMORY_readmap[0x5f] = CARTRIDGE_BountyBob2GetByte; MEMORY_writemap[0x4f] = CARTRIDGE_BountyBob1PutByte; MEMORY_writemap[0x5f] = CARTRIDGE_BountyBob2PutByte; #endif break; case CARTRIDGE_5200_NS_16: MEMORY_CopyROM(0x8000, 0xbfff, active_cart->image); break; case CARTRIDGE_5200_8: MEMORY_CopyROM(0x8000, 0x9fff, active_cart->image); MEMORY_CopyROM(0xa000, 0xbfff, active_cart->image); break; case CARTRIDGE_5200_4: MEMORY_CopyROM(0x8000, 0x8fff, active_cart->image); MEMORY_CopyROM(0x9000, 0x9fff, active_cart->image); MEMORY_CopyROM(0xa000, 0xafff, active_cart->image); MEMORY_CopyROM(0xb000, 0xbfff, active_cart->image); break; default: /* clear cartridge area so the 5200 will crash */ MEMORY_dFillMem(0x4000, 0, 0x8000); break; } } else { switch (active_cart->type) { case CARTRIDGE_STD_2: MEMORY_Cart809fDisable(); MEMORY_CartA0bfEnable(); MEMORY_dFillMem(0xa000, 0xff, 0x1800); MEMORY_CopyROM(0xb800, 0xbfff, active_cart->image); break; case CARTRIDGE_STD_4: MEMORY_Cart809fDisable(); MEMORY_CartA0bfEnable(); MEMORY_dFillMem(0xa000, 0xff, 0x1000); MEMORY_CopyROM(0xb000, 0xbfff, active_cart->image); break; case CARTRIDGE_BLIZZARD_4: MEMORY_Cart809fDisable(); MEMORY_CartA0bfEnable(); MEMORY_CopyROM(0xa000, 0xafff, active_cart->image); MEMORY_CopyROM(0xb000, 0xbfff, active_cart->image); break; case CARTRIDGE_STD_8: case CARTRIDGE_PHOENIX_8: MEMORY_Cart809fDisable(); MEMORY_CartA0bfEnable(); MEMORY_CopyROM(0xa000, 0xbfff, active_cart->image); break; case CARTRIDGE_LOW_BANK_8: MEMORY_Cart809fEnable(); MEMORY_CartA0bfDisable(); MEMORY_CopyROM(0x8000, 0x9fff, active_cart->image); break; case CARTRIDGE_STD_16: case CARTRIDGE_BLIZZARD_16: MEMORY_Cart809fEnable(); MEMORY_CartA0bfEnable(); MEMORY_CopyROM(0x8000, 0xbfff, active_cart->image); break; case CARTRIDGE_OSS_034M_16: case CARTRIDGE_OSS_043M_16: MEMORY_Cart809fDisable(); if (active_cart->state >= 0) { MEMORY_CartA0bfEnable(); MEMORY_CopyROM(0xb000, 0xbfff, active_cart->image + 0x3000); } break; case CARTRIDGE_OSS_M091_16: case CARTRIDGE_OSS_8: MEMORY_Cart809fDisable(); if (active_cart->state >= 0) { MEMORY_CartA0bfEnable(); MEMORY_CopyROM(0xb000, 0xbfff, active_cart->image); } break; case CARTRIDGE_WILL_64: case CARTRIDGE_EXP_64: case CARTRIDGE_DIAMOND_64: case CARTRIDGE_SDX_64: case CARTRIDGE_ATRAX_128: case CARTRIDGE_WILL_32: case CARTRIDGE_ATMAX_128: case CARTRIDGE_ATMAX_1024: case CARTRIDGE_SDX_128: case CARTRIDGE_ATRAX_SDX_64: case CARTRIDGE_ATRAX_SDX_128: case CARTRIDGE_TURBOSOFT_64: case CARTRIDGE_TURBOSOFT_128: case CARTRIDGE_ULTRACART_32: case CARTRIDGE_BLIZZARD_32: case CARTRIDGE_THECART_128M: case CARTRIDGE_THECART_32M: case CARTRIDGE_THECART_64M: MEMORY_Cart809fDisable(); break; case CARTRIDGE_DB_32: case CARTRIDGE_XEGS_32: case CARTRIDGE_SWXEGS_32: if (!(active_cart->state & 0x80)) { MEMORY_CartA0bfEnable(); MEMORY_CopyROM(0xa000, 0xbfff, active_cart->image + 0x6000); } break; case CARTRIDGE_XEGS_07_64: case CARTRIDGE_SWXEGS_64: case CARTRIDGE_XEGS_8F_64: if (!(active_cart->state & 0x80)) { MEMORY_CartA0bfEnable(); MEMORY_CopyROM(0xa000, 0xbfff, active_cart->image + 0xe000); } break; case CARTRIDGE_XEGS_128: case CARTRIDGE_SWXEGS_128: if (!(active_cart->state & 0x80)) { MEMORY_CartA0bfEnable(); MEMORY_CopyROM(0xa000, 0xbfff, active_cart->image + 0x1e000); } break; case CARTRIDGE_XEGS_256: case CARTRIDGE_SWXEGS_256: if (!(active_cart->state & 0x80)) { MEMORY_CartA0bfEnable(); MEMORY_CopyROM(0xa000, 0xbfff, active_cart->image + 0x3e000); } break; case CARTRIDGE_XEGS_512: case CARTRIDGE_SWXEGS_512: if (!(active_cart->state & 0x80)) { MEMORY_CartA0bfEnable(); MEMORY_CopyROM(0xa000, 0xbfff, active_cart->image + 0x7e000); } break; case CARTRIDGE_XEGS_1024: case CARTRIDGE_SWXEGS_1024: if (!(active_cart->state & 0x80)) { MEMORY_CartA0bfEnable(); MEMORY_CopyROM(0xa000, 0xbfff, active_cart->image + 0xfe000); } break; case CARTRIDGE_BBSB_40: MEMORY_Cart809fEnable(); MEMORY_CartA0bfEnable(); MEMORY_CopyROM(0x8000, 0x8fff, active_cart->image + (active_cart->state & 0x03) * 0x1000); MEMORY_CopyROM(0x9000, 0x9fff, active_cart->image + 0x4000 + ((active_cart->state & 0x0c) >> 2) * 0x1000); MEMORY_CopyROM(0xa000, 0xbfff, active_cart->image + 0x8000); #ifndef PAGED_ATTRIB MEMORY_SetHARDWARE(0x8ff6, 0x8ff9); MEMORY_SetHARDWARE(0x9ff6, 0x9ff9); #else MEMORY_readmap[0x8f] = CARTRIDGE_BountyBob1GetByte; MEMORY_readmap[0x9f] = CARTRIDGE_BountyBob2GetByte; MEMORY_writemap[0x8f] = CARTRIDGE_BountyBob1PutByte; MEMORY_writemap[0x9f] = CARTRIDGE_BountyBob2PutByte; #endif /* No need to call SwitchBank(), return. */ return; case CARTRIDGE_RIGHT_4: if (Atari800_machine_type == Atari800_MACHINE_800) { MEMORY_Cart809fEnable(); MEMORY_dFillMem(0x8000, 0xff, 0x1000); MEMORY_CopyROM(0x9000, 0x9fff, active_cart->image); if ((!Atari800_disable_basic || BINLOAD_loading_basic) && MEMORY_have_basic) { MEMORY_CartA0bfEnable(); MEMORY_CopyROM(0xa000, 0xbfff, MEMORY_basic); } else MEMORY_CartA0bfDisable(); } else { /* there's no right slot in XL/XE */ MEMORY_Cart809fDisable(); MEMORY_CartA0bfDisable(); } /* No need to call SwitchBank(), return. */ return; case CARTRIDGE_RIGHT_8: if (Atari800_machine_type == Atari800_MACHINE_800) { MEMORY_Cart809fEnable(); MEMORY_CopyROM(0x8000, 0x9fff, active_cart->image); if (!Atari800_builtin_basic && (!Atari800_disable_basic || BINLOAD_loading_basic) && MEMORY_have_basic) { MEMORY_CartA0bfEnable(); MEMORY_CopyROM(0xa000, 0xbfff, MEMORY_basic); } else MEMORY_CartA0bfDisable(); } else { /* there's no right slot in XL/XE */ MEMORY_Cart809fDisable(); MEMORY_CartA0bfDisable(); } /* No need to call SwitchBank(), return. */ return; case CARTRIDGE_AST_32: { int i; MEMORY_Cart809fDisable(); MEMORY_CartA0bfEnable(); /* Copy the chosen bank 32 times over 0xa000-0xbfff. */ for (i = 0xa000; i < 0xc000; i += 0x100) MEMORY_CopyROM(i, i + 0xff, active_cart->image + (active_cart->state & 0xffff)); } break; case CARTRIDGE_MEGA_16: case CARTRIDGE_MEGA_32: case CARTRIDGE_MEGA_64: case CARTRIDGE_MEGA_128: case CARTRIDGE_MEGA_256: case CARTRIDGE_MEGA_512: case CARTRIDGE_MEGA_1024: case CARTRIDGE_MEGA_2048: case CARTRIDGE_MEGA_4096: case CARTRIDGE_SIC_128: case CARTRIDGE_SIC_256: case CARTRIDGE_SIC_512: case CARTRIDGE_MEGAMAX_2048: break; default: MEMORY_Cart809fDisable(); if (!Atari800_builtin_basic && (!Atari800_disable_basic || BINLOAD_loading_basic) && MEMORY_have_basic) { MEMORY_CartA0bfEnable(); MEMORY_CopyROM(0xa000, 0xbfff, MEMORY_basic); } else MEMORY_CartA0bfDisable(); /* No need to call SwitchBank(), return. */ return; } SwitchBank(active_cart->state); } } /* Called from GetByte() and PutByte(), this function sets cartridge state for a cartridge that is bank-switched by either read or write to page $D5. Returns TRUE if switching a bank is needed. */ static int access_D5(CARTRIDGE_image_t *cart, UWORD addr, int *state) { int old_state = cart->state; int new_state; switch (cart->type) { case CARTRIDGE_OSS_034M_16: /* Reference: http://www.retrobits.net/atari/osscarts.shtml Deprecated by CARTRIDGE_OSS_043M_16 - 034M is an incorrect bank order (a real cartridge consists of two 8KB chips, one containing banks 0 and 4, the other 3 and M). Kept here for backward compatibility. */ if (addr & 0x08) new_state = -1; else switch (addr & 0x07) { case 0x00: /* B Lo/A Hi */ new_state = 0; break; case 0x01: /* A Lo+B Lo/A Hi */ /* TODO should be binary AND of both banks. For now only fills with 0xFFs. */ new_state = 0xff; break; case 0x03: case 0x07: /* A Lo/A Hi */ new_state = 1; break; case 0x04: /* B Hi/A Hi */ new_state = 2; break; case 0x05: /* A Lo+B Hi/A Hi */ /* TODO should be binary AND of both banks. For now only fills with 0xFFs. */ new_state = 0xff; break; default: /* 0x02, 0x06 */ /* Fill cart area with 0xFFs. */ new_state = 0xff; break; } break; case CARTRIDGE_OSS_043M_16: /* Reference: http://www.retrobits.net/atari/osscarts.shtml Using the nomenclature of the above article: the emulator accepts 16KB images composed of two 8KB EPROM dumps joined together in the following order: ROM B, ROM A. Currently only three cartridges with this scheme are known: Action! 3.5, BASIC XL 1.02 and MAC/65 1.0. */ if (addr & 0x08) new_state = -1; else switch (addr & 0x07) { case 0x00: /* B Lo/A Hi */ new_state = 0; break; case 0x01: /* A Lo+B Lo/A Hi */ /* TODO should be binary AND of both banks. For now only fills with 0xFFs. */ new_state = 0xff; break; case 0x03: case 0x07: /* A Lo/A Hi */ new_state = 2; break; case 0x04: /* B Hi/A Hi */ new_state = 1; break; case 0x05: /* A Lo+B Hi/A Hi */ /* TODO should be binary AND of both banks. For now only fills with 0xFFs. */ new_state = 0xff; break; default: /* 0x02, 0x06 */ /* Fill cart area with 0xFFs. */ new_state = 0xff; break; } break; case CARTRIDGE_DB_32: new_state = addr & 0x03; break; case CARTRIDGE_WILL_64: new_state = addr & 0x0f; break; case CARTRIDGE_WILL_32: new_state = addr & 0x0b; break; case CARTRIDGE_EXP_64: /* Only react to access to $D57x. */ if ((addr & 0xf0) != 0x70) return FALSE; new_state = ((addr ^ 7) & 0x0f); break; case CARTRIDGE_DIAMOND_64: /* Only react to access to $D5Dx. */ if ((addr & 0xf0) != 0xd0) return FALSE; new_state = ((addr ^ 7) & 0x0f); break; case CARTRIDGE_SDX_64: case CARTRIDGE_ATRAX_SDX_64: /* Only react to access to $D5Ex. */ if ((addr & 0xf0) != 0xe0) return FALSE; if (addr & 0x08) new_state = addr & 0x0c; else /* Negate bits that encode bank number. */ new_state = ((addr ^ 0x07) & 0x0f); if (cart == &CARTRIDGE_main) { /* It's the 1st cartridge, process switching the piggyback on/off. */ if ((old_state & 0x0c) == 0x08) { /* Piggyback cartridge was enabled */ if ((new_state & 0x0c) != 0x08) { /* Going to disable it */ active_cart = &CARTRIDGE_main; MapActiveCart(); } } else if ((new_state & 0x0c) == 0x08) { /* Going to enable piggyback */ active_cart = &CARTRIDGE_piggyback; MapActiveCart(); } } break; case CARTRIDGE_SDX_128: case CARTRIDGE_ATRAX_SDX_128: /* Only react to access to $D5Ex/$D5Fx. */ if ((addr & 0xe0) != 0xe0) return FALSE; if (addr & 0x08) new_state = addr & 0x0c; else /* Negate bits that encode bank number. */ new_state = ((addr ^ 0x17) & 0x1f); if (cart == &CARTRIDGE_main) { /* It's the 1st cartridge, process switching the piggyback on/off. */ if ((old_state & 0x0c) == 0x08) { /* Piggyback cartridge was enabled */ if ((new_state & 0x0c) != 0x08) { /* Going to disable it */ active_cart = &CARTRIDGE_main; MapActiveCart(); } } else if ((new_state & 0x0c) == 0x08) { /* Going to enable piggyback */ active_cart = &CARTRIDGE_piggyback; MapActiveCart(); } } break; case CARTRIDGE_OSS_M091_16: switch (addr & 0x09) { case 0x00: new_state = 1; break; case 0x01: new_state = 3; break; case 0x08: new_state = -1; break; default: /* 0x09 */ new_state = 2; break; } break; case CARTRIDGE_BLIZZARD_4: case CARTRIDGE_PHOENIX_8: case CARTRIDGE_BLIZZARD_16: /* Disable the cart. */ new_state = 1; break; case CARTRIDGE_ATMAX_128: /* Only react to access to $D50x/$D51x. */ if ((addr & 0xe0) != 0) return FALSE; /* fall through */ case CARTRIDGE_TURBOSOFT_128: new_state = addr & 0x1f; break; case CARTRIDGE_TURBOSOFT_64: new_state = addr & 0x17; break; case CARTRIDGE_ATMAX_1024: case CARTRIDGE_MEGAMAX_2048: new_state = addr; break; case CARTRIDGE_OSS_8: switch (addr & 0x09) { case 0x00: case 0x01: new_state = 1; break; case 0x08: new_state = -1; break; default: /* 0x09 */ new_state = 0; break; } break; case CARTRIDGE_ULTRACART_32: new_state = (old_state + 1) % 5; break; case CARTRIDGE_BLIZZARD_32: if (old_state < 4) new_state = old_state + 1; else default: /* Other cartridge types don't support enabling/disabling/banking through page D5. */ return FALSE; } *state = new_state; return TRUE; } /* Processes bankswitching of CART when reading from a $D5xx address ADDR. */ static UBYTE GetByte(CARTRIDGE_image_t *cart, UWORD addr, int no_side_effects) { int old_state = cart->state; int new_state = old_state; #if DEBUG if (cart->type > CARTRIDGE_NONE) Log_print("Cart %i read: %04x", cart == &CARTRIDGE_piggyback, addr); #endif /* Set the cartridge's new state. */ /* Check types switchable by access to page D5. */ if (!no_side_effects && access_D5(cart, addr, &new_state)) { /* Cartridge supports bankswitching and reacted to the given ADDR. If the state changed, we need to do the bankswitch. */ if (new_state != old_state) { cart->state = new_state; if (cart == active_cart) SwitchBank(old_state); } } /* Determine returned byte value. */ switch (cart->type) { case CARTRIDGE_AST_32: /* cart->state contains address of current bank, therefore it divides by 0x100. */ return cart->image[(cart->state & 0xff00) | (addr & 0xff)]; case CARTRIDGE_SIC_512: case CARTRIDGE_SIC_256: case CARTRIDGE_SIC_128: case CARTRIDGE_MEGA_4096: /* Only react to access to $D50x/$D51x. */ if ((addr & 0xe0) == 0x00) return cart->state; break; case CARTRIDGE_THECART_128M: case CARTRIDGE_THECART_32M: case CARTRIDGE_THECART_64M: switch (addr) { case 0xd5a0: return cart->state & 0x00ff; case 0xd5a1: return (cart->state & 0x3f00) >> 8; case 0xd5a2: return (~cart->state & 0x4000) >> 14; } break; } return 0xff; } /* Processes bankswitching of CART when writing to a $D5xx address ADDR. */ static void PutByte(CARTRIDGE_image_t *cart, UWORD addr, UBYTE byte) { int old_state = cart->state; int new_state = old_state; #if DEBUG if (cart->type > CARTRIDGE_NONE) Log_print("Cart %i write: %04x, %02x", cart == &CARTRIDGE_piggyback, addr, byte); #endif /* Set the cartridge's new state. */ switch (cart->type) { case CARTRIDGE_XEGS_32: new_state = byte & 0x03; break; case CARTRIDGE_XEGS_07_64: new_state = byte & 0x07; break; case CARTRIDGE_XEGS_128: case CARTRIDGE_XEGS_8F_64: new_state = byte & 0x0f; break; case CARTRIDGE_XEGS_256: new_state = byte & 0x1f; break; case CARTRIDGE_XEGS_512: new_state = byte & 0x3f; break; case CARTRIDGE_XEGS_1024: new_state = byte & 0x7f; break; case CARTRIDGE_MEGA_16: new_state = byte & 0x80; break; case CARTRIDGE_MEGA_32: new_state = byte & 0x81; break; case CARTRIDGE_MEGA_64: case CARTRIDGE_SWXEGS_32: new_state = byte & 0x83; break; case CARTRIDGE_MEGA_128: case CARTRIDGE_SWXEGS_64: new_state = byte & 0x87; break; case CARTRIDGE_MEGA_256: case CARTRIDGE_SWXEGS_128: case CARTRIDGE_ATRAX_128: new_state = byte & 0x8f; break; case CARTRIDGE_MEGA_512: case CARTRIDGE_SWXEGS_256: new_state = byte & 0x9f; break; case CARTRIDGE_MEGA_1024: case CARTRIDGE_SWXEGS_512: new_state = byte & 0xbf; break; case CARTRIDGE_MEGA_2048: case CARTRIDGE_SWXEGS_1024: new_state = byte; break; case CARTRIDGE_AST_32: /* State contains address of current bank. */ new_state = (old_state + 0x100) & 0x7fff; break; case CARTRIDGE_SIC_512: case CARTRIDGE_SIC_256: case CARTRIDGE_SIC_128: case CARTRIDGE_MEGA_4096: /* Only react to access to $D50x/$D51x. */ if ((addr & 0xe0) == 0x00) new_state = byte; break; case CARTRIDGE_THECART_128M: case CARTRIDGE_THECART_32M: case CARTRIDGE_THECART_64M: switch (addr) { case 0xd5a0: new_state = (old_state & 0x3f00) | byte; break; case 0xd5a1: new_state = (old_state & 0x00ff) | ((byte & 0x3f) << 8); break; case 0xd5a2: new_state = (old_state & 0x3fff) | ((~byte & 0x01) << 14); break; } break; default: /* Check types switchable by access to page D5. */ if (!access_D5(cart, addr, &new_state)) /* Cartridge doesn't support bankswitching, or didn't react to the given ADDR. */ return; } /* If the state changed, we need to do the bankswitch. */ if (new_state != old_state) { cart->state = new_state; if (cart == active_cart) SwitchBank(old_state); } } /* a read from D500-D5FF area */ UBYTE CARTRIDGE_GetByte(UWORD addr, int no_side_effects) { #ifdef AF80 if (AF80_enabled) { return AF80_D5GetByte(addr, no_side_effects); } #endif #ifdef BIT3 if (BIT3_enabled) { return BIT3_D5GetByte(addr, no_side_effects); } #endif if (RTIME_enabled && (addr == 0xd5b8 || addr == 0xd5b9)) return RTIME_GetByte(); #ifdef IDE if (IDE_enabled && (addr <= 0xd50f)) return IDE_GetByte(addr, no_side_effects); #endif /* In case 2 cartridges are inserted, reading a memory location would result in binary AND of both cartridges. */ return GetByte(&CARTRIDGE_main, addr, no_side_effects) & GetByte(&CARTRIDGE_piggyback, addr, no_side_effects); } /* a write to D500-D5FF area */ void CARTRIDGE_PutByte(UWORD addr, UBYTE byte) { #ifdef AF80 if (AF80_enabled) { AF80_D5PutByte(addr,byte); /* Return, because AF_80_enabled means there's an AF80 cartridge in the left slot and no other cartridges are there. */ return; } #endif #ifdef BIT3 if (BIT3_enabled && (addr == 0xd508 || addr == 0xd580 || addr == 0xd581 || addr == 0xd583 || addr == 0xd585)) { BIT3_D5PutByte(addr,byte); } #endif if (RTIME_enabled && (addr == 0xd5b8 || addr == 0xd5b9)) { RTIME_PutByte(byte); } #ifdef IDE if (IDE_enabled && (addr <= 0xd50f)) { IDE_PutByte(addr,byte); } #endif PutByte(&CARTRIDGE_main, addr, byte); PutByte(&CARTRIDGE_piggyback, addr, byte); } /* special support of Bounty Bob on Atari5200 */ void CARTRIDGE_BountyBob1(UWORD addr) { if (Atari800_machine_type == Atari800_MACHINE_5200) { if (addr >= 0x4ff6 && addr <= 0x4ff9) { addr -= 0x4ff6; MEMORY_CopyROM(0x4000, 0x4fff, active_cart->image + addr * 0x1000); active_cart->state = (active_cart->state & 0x0c) | addr; } } else { if (addr >= 0x8ff6 && addr <= 0x8ff9) { addr -= 0x8ff6; MEMORY_CopyROM(0x8000, 0x8fff, active_cart->image + addr * 0x1000); active_cart->state = (active_cart->state & 0x0c) | addr; } } } void CARTRIDGE_BountyBob2(UWORD addr) { if (Atari800_machine_type == Atari800_MACHINE_5200) { if (addr >= 0x5ff6 && addr <= 0x5ff9) { addr -= 0x5ff6; MEMORY_CopyROM(0x5000, 0x5fff, active_cart->image + 0x4000 + addr * 0x1000); active_cart->state = (active_cart->state & 0x03) | (addr << 2); } } else { if (addr >= 0x9ff6 && addr <= 0x9ff9) { addr -= 0x9ff6; MEMORY_CopyROM(0x9000, 0x9fff, active_cart->image + 0x4000 + addr * 0x1000); active_cart->state = (active_cart->state & 0x03) | (addr << 2); } } } #ifdef PAGED_ATTRIB UBYTE CARTRIDGE_BountyBob1GetByte(UWORD addr, int no_side_effects) { if (!no_side_effects) { if (Atari800_machine_type == Atari800_MACHINE_5200) { if (addr >= 0x4ff6 && addr <= 0x4ff9) { CARTRIDGE_BountyBob1(addr); return 0; } } else { if (addr >= 0x8ff6 && addr <= 0x8ff9) { CARTRIDGE_BountyBob1(addr); return 0; } } } return MEMORY_dGetByte(addr); } UBYTE CARTRIDGE_BountyBob2GetByte(UWORD addr, int no_side_effects) { if (!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; } } } return MEMORY_dGetByte(addr); } void CARTRIDGE_BountyBob1PutByte(UWORD addr, UBYTE value) { if (Atari800_machine_type == Atari800_MACHINE_5200) { if (addr >= 0x4ff6 && addr <= 0x4ff9) { CARTRIDGE_BountyBob1(addr); } } else { if (addr >= 0x8ff6 && addr <= 0x8ff9) { CARTRIDGE_BountyBob1(addr); } } } void CARTRIDGE_BountyBob2PutByte(UWORD addr, UBYTE value) { if (Atari800_machine_type == Atari800_MACHINE_5200) { if (addr >= 0x5ff6 && addr <= 0x5ff9) { CARTRIDGE_BountyBob2(addr); } } else { if (addr >= 0x9ff6 && addr <= 0x9ff9) { CARTRIDGE_BountyBob2(addr); } } } #endif int CARTRIDGE_Checksum(const UBYTE *image, int nbytes) { int checksum = 0; while (nbytes > 0) { checksum += *image++; nbytes--; } return checksum; } static void ResetCartState(CARTRIDGE_image_t *cart) { switch (cart->type) { case CARTRIDGE_OSS_034M_16: cart->state = 1; break; case CARTRIDGE_ATMAX_1024: cart->state = 0x7f; break; case CARTRIDGE_AST_32: /* A special value of 0x10000 indicates the cartridge is enabled and the current bank is 0. */ cart->state = 0x10000; break; case CARTRIDGE_MEGA_4096: cart->state = 254; break; default: cart->state = 0; } } /* Before first use of the cartridge, preprocess its contents if needed. */ static void PreprocessCart(CARTRIDGE_image_t *cart) { switch (cart->type) { case CARTRIDGE_ATRAX_SDX_64: case CARTRIDGE_ATRAX_SDX_128: { /* The address lines are connected a follows: (left - cartridge port + bank select, right - EPROM) A0 - A6 A1 - A7 A2 - A12 A3 - A15 A4 - A14 A5 - A13 A6 - A8 A7 - A5 A8 - A4 A9 - A3 A10 - A0 A11 - A1 A12 - A2 A13 - A9 A14 - A11 A15 - A10 A16 - A16 (only on ATRAX_SDX_128) The data lines are connected as follows: (left - cartridge port, right - EPROM) D1 - Q0 D3 - Q1 D7 - Q2 D6 - Q3 D0 - Q4 D2 - Q5 D5 - Q6 D4 - Q7 */ unsigned int i; unsigned int const size = cart->size << 10; UBYTE *new_image = (UBYTE *) Util_malloc(size); /* FIXME: Can be optimised by caching the results in a conversion table, but doesn't seem to be worth it. */ for (i = 0; i < size; i++) { unsigned int const rom_addr = (i & 0x0001 ? 0x0040 : 0) | (i & 0x0002 ? 0x0080 : 0) | (i & 0x0004 ? 0x1000 : 0) | (i & 0x0008 ? 0x8000 : 0) | (i & 0x0010 ? 0x4000 : 0) | (i & 0x0020 ? 0x2000 : 0) | (i & 0x0040 ? 0x0100 : 0) | (i & 0x0080 ? 0x0020 : 0) | (i & 0x0100 ? 0x0010 : 0) | (i & 0x0200 ? 0x0008 : 0) | (i & 0x0400 ? 0x0001 : 0) | (i & 0x0800 ? 0x0002 : 0) | (i & 0x1000 ? 0x0004 : 0) | (i & 0x2000 ? 0x0200 : 0) | (i & 0x4000 ? 0x0800 : 0) | (i & 0x8000 ? 0x0400 : 0) | (i & 0x10000 ? 0x10000 : 0); UBYTE byte = cart->image[rom_addr]; new_image[i] = (byte & 0x01 ? 0x02 : 0) | (byte & 0x02 ? 0x08 : 0) | (byte & 0x04 ? 0x80 : 0) | (byte & 0x08 ? 0x40 : 0) | (byte & 0x10 ? 0x01 : 0) | (byte & 0x20 ? 0x04 : 0) | (byte & 0x40 ? 0x20 : 0) | (byte & 0x80 ? 0x10 : 0); } free(cart->image); cart->image = new_image; break; } } } /* Initialises the cartridge CART after mounting. Called by CARTRIDGE_Insert, or CARTRIDGE_Insert_Second and CARTRIDGE_SetType. */ static void InitCartridge(CARTRIDGE_image_t *cart) { PreprocessCart(cart); ResetCartState(cart); if (cart == &CARTRIDGE_main) { /* Check if we should automatically switch between computer/5200. */ int for5200 = CartIsFor5200(CARTRIDGE_main.type); if (for5200 && Atari800_machine_type != Atari800_MACHINE_5200) { Atari800_SetMachineType(Atari800_MACHINE_5200); MEMORY_ram_size = 16; Atari800_InitialiseMachine(); } else if (!for5200 && Atari800_machine_type == Atari800_MACHINE_5200) { Atari800_SetMachineType(Atari800_MACHINE_XLXE); MEMORY_ram_size = 64; Atari800_InitialiseMachine(); } } if (cart == active_cart) MapActiveCart(); } static void RemoveCart(CARTRIDGE_image_t *cart) { if (cart->image != NULL) { free(cart->image); cart->image = NULL; } if (cart->type != CARTRIDGE_NONE) { cart->type = CARTRIDGE_NONE; if (cart == active_cart) MapActiveCart(); } } /* Called after inserting/removing a cartridge (but not the piggyback one). If needed, reboots the machine. */ static void AutoReboot(void) { if (CARTRIDGE_autoreboot) Atari800_Coldstart(); } void CARTRIDGE_SetType(CARTRIDGE_image_t *cart, int type) { cart->type = type; if (type == CARTRIDGE_NONE) /* User cancelled setting the cartridge's type - the cartridge can be unloaded. */ RemoveCart(cart); InitCartridge(cart); } void CARTRIDGE_SetTypeAutoReboot(CARTRIDGE_image_t *cart, int type) { CARTRIDGE_SetType(cart, type); /* We don't want to autoreboot on inserting the piggyback cartridge. */ if (cart != &CARTRIDGE_piggyback) AutoReboot(); } void CARTRIDGE_ColdStart(void) { active_cart = &CARTRIDGE_main; ResetCartState(&CARTRIDGE_main); ResetCartState(&CARTRIDGE_piggyback); MapActiveCart(); } #ifdef __LIBRETRO__ #include "atari5200_hash.h" extern int autorun5200; #endif /* 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. */ static int InsertCartridge(const char *filename, CARTRIDGE_image_t *cart) { FILE *fp; int len; int type; UBYTE header[16]; #ifdef __LIBRETRO__ ULONG crc; #endif /* open file */ fp = fopen(filename, "rb"); if (fp == NULL) return CARTRIDGE_CANT_OPEN; /* check file length */ len = Util_flen(fp); Util_rewind(fp); #ifdef __LIBRETRO__ if(autorun5200){ CRC32_FromFile(fp, &crc); Util_rewind(fp); } #endif /* Guard against providing cart->filename as parameter. */ if (cart->filename != filename) /* Save Filename for state save */ strcpy(cart->filename, filename); /* if full kilobytes, assume it is raw image */ if ((len & 0x3ff) == 0) { /* alloc memory and read data */ cart->image = (UBYTE *) Util_malloc(len); if (fread(cart->image, 1, len, fp) < len) { Log_print("Error reading cartridge.\n"); } fclose(fp); /* find cart type */ cart->type = CARTRIDGE_NONE; 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; } 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); break; } i++; } if(match==1)goto label_fin; } #endif for (type = 1; type <= CARTRIDGE_LAST_SUPPORTED; type++) if (CARTRIDGE_kb[type] == len) { if (cart->type == CARTRIDGE_NONE) { cart->type = type; } else { /* more than one cartridge type of such length - user must select */ cart->type = CARTRIDGE_UNKNOWN; return len; } } #ifdef __LIBRETRO__ label_fin: #endif if (cart->type != CARTRIDGE_NONE) { InitCartridge(cart); return 0; /* ok */ } free(cart->image); cart->image = NULL; return CARTRIDGE_BAD_FORMAT; } /* if not full kilobytes, assume it is CART file */ if (fread(header, 1, 16, fp) < 16) { Log_print("Error reading cartridge.\n"); } if ((header[0] == 'C') && (header[1] == 'A') && (header[2] == 'R') && (header[3] == 'T')) { type = (header[4] << 24) | (header[5] << 16) | (header[6] << 8) | header[7]; if (type >= 1 && type <= CARTRIDGE_LAST_SUPPORTED) { int checksum; int result; len = CARTRIDGE_kb[type] << 10; cart->size = CARTRIDGE_kb[type]; /* alloc memory and read data */ cart->image = (UBYTE *) Util_malloc(len); if (fread(cart->image, 1, len, fp) < len) { Log_print("Error reading cartridge.\n"); } fclose(fp); checksum = (header[8] << 24) | (header[9] << 16) | (header[10] << 8) | header[11]; cart->type = type; result = checksum == CARTRIDGE_Checksum(cart->image, len) ? 0 : CARTRIDGE_BAD_CHECKSUM; InitCartridge(cart); return result; } } fclose(fp); return CARTRIDGE_BAD_FORMAT; } int CARTRIDGE_Insert(const char *filename) { /* remove currently inserted cart */ CARTRIDGE_Remove(); return InsertCartridge(filename, &CARTRIDGE_main); } int CARTRIDGE_InsertAutoReboot(const char *filename) { int result = CARTRIDGE_Insert(filename); AutoReboot(); return result; } int CARTRIDGE_Insert_Second(const char *filename) { /* remove currently inserted cart */ CARTRIDGE_Remove_Second(); return InsertCartridge(filename, &CARTRIDGE_piggyback); } void CARTRIDGE_Remove(void) { active_cart = &CARTRIDGE_main; CARTRIDGE_Remove_Second(); RemoveCart(&CARTRIDGE_main); } void CARTRIDGE_RemoveAutoReboot(void) { CARTRIDGE_Remove(); AutoReboot(); } void CARTRIDGE_Remove_Second(void) { RemoveCart(&CARTRIDGE_piggyback); } int CARTRIDGE_ReadConfig(char *string, char *ptr) { if (strcmp(string, "CARTRIDGE_FILENAME") == 0) { Util_strlcpy(CARTRIDGE_main.filename, ptr, sizeof(CARTRIDGE_main.filename)); if (CARTRIDGE_main.type == CARTRIDGE_NONE) CARTRIDGE_main.type = CARTRIDGE_UNKNOWN; } else if (strcmp(string, "CARTRIDGE_TYPE") == 0) { int value = Util_sscandec(ptr); if (value < 0 || value > CARTRIDGE_LAST_SUPPORTED) return FALSE; CARTRIDGE_main.type = value; } else if (strcmp(string, "CARTRIDGE_PIGGYBACK_FILENAME") == 0) { Util_strlcpy(CARTRIDGE_piggyback.filename, ptr, sizeof(CARTRIDGE_piggyback.filename)); if (CARTRIDGE_piggyback.type == CARTRIDGE_NONE) CARTRIDGE_piggyback.type = CARTRIDGE_UNKNOWN; } else if (strcmp(string, "CARTRIDGE_PIGGYBACK_TYPE") == 0) { int value = Util_sscandec(ptr); if (value < 0 || value > CARTRIDGE_LAST_SUPPORTED) return FALSE; CARTRIDGE_piggyback.type = value; } else if (strcmp(string, "CARTRIDGE_AUTOREBOOT") == 0) { int value = Util_sscanbool(ptr); if (value < 0) return FALSE; CARTRIDGE_autoreboot = value; } else return FALSE; return TRUE; } void CARTRIDGE_WriteConfig(FILE *fp) { fprintf(fp, "CARTRIDGE_FILENAME=%s\n", CARTRIDGE_main.filename); fprintf(fp, "CARTRIDGE_TYPE=%d\n", CARTRIDGE_main.type); fprintf(fp, "CARTRIDGE_PIGGYBACK_FILENAME=%s\n", CARTRIDGE_piggyback.filename); fprintf(fp, "CARTRIDGE_PIGGYBACK_TYPE=%d\n", CARTRIDGE_piggyback.type); fprintf(fp, "CARTRIDGE_AUTOREBOOT=%d\n", CARTRIDGE_autoreboot); } static void InitInsert(CARTRIDGE_image_t *cart) { if (cart->type != CARTRIDGE_NONE) { int tmp_type = cart->type; int res = InsertCartridge(cart->filename, cart); if (res < 0) { Log_print("Error inserting cartridge \"%s\": %s", cart->filename, res == CARTRIDGE_CANT_OPEN ? "Can't open file" : res == CARTRIDGE_BAD_FORMAT ? "Bad format" : /* Assume r == CARTRIDGE_BAD_CHECKSUM */ "Bad checksum"); cart->type = CARTRIDGE_NONE; } if (cart->type == CARTRIDGE_UNKNOWN && CARTRIDGE_kb[tmp_type] == res) CARTRIDGE_SetType(cart, tmp_type); } } int CARTRIDGE_Initialise(int *argc, char *argv[]) { int i; int j; int help_only = FALSE; /* When filename is given at commandline, we have to reset cartridge type to UNKNOWN, because the cartridge type read earlier from the config file is no longer valid. These two variables indicate that cartridge type is also given at commandline and so it shouldn't be reset. */ int type_from_commandline = FALSE; int type2_from_commandline = FALSE; for (i = j = 1; i < *argc; i++) { int i_a = (i + 1 < *argc); /* is argument available? */ int a_m = FALSE; /* error, argument missing! */ int a_i = FALSE; /* error, argument invalid! */ if (strcmp(argv[i], "-cart") == 0) { if (i_a) { Util_strlcpy(CARTRIDGE_main.filename, argv[++i], sizeof(CARTRIDGE_main.filename)); if (!type_from_commandline) CARTRIDGE_main.type = CARTRIDGE_UNKNOWN; } else a_m = TRUE; } else if (strcmp(argv[i], "-cart-type") == 0) { if (i_a) { Util_sscansdec(argv[++i], &CARTRIDGE_main.type); if (CARTRIDGE_main.type < 0 || CARTRIDGE_main.type > CARTRIDGE_LAST_SUPPORTED) a_i = TRUE; else type_from_commandline = TRUE; } else a_m = TRUE; } else if (strcmp(argv[i], "-cart2") == 0) { if (i_a) { Util_strlcpy(CARTRIDGE_piggyback.filename, argv[++i], sizeof(CARTRIDGE_piggyback.filename)); if (!type2_from_commandline) CARTRIDGE_piggyback.type = CARTRIDGE_UNKNOWN; } else a_m = TRUE; } else if (strcmp(argv[i], "-cart2-type") == 0) { if (i_a) { Util_sscansdec(argv[++i], &CARTRIDGE_piggyback.type); if (CARTRIDGE_piggyback.type < 0 || CARTRIDGE_piggyback.type > CARTRIDGE_LAST_SUPPORTED) a_i = TRUE; else type2_from_commandline = TRUE; } else a_m = TRUE; } else if (strcmp(argv[i], "-cart-autoreboot") == 0) CARTRIDGE_autoreboot = TRUE; else if (strcmp(argv[i], "-no-cart-autoreboot") == 0) CARTRIDGE_autoreboot = FALSE; else { if (strcmp(argv[i], "-help") == 0) { help_only = TRUE; Log_print("\t-cart Install cartridge (raw or CART format)"); Log_print("\t-cart-type Set cartridge type (0..%i)", CARTRIDGE_LAST_SUPPORTED); Log_print("\t-cart2 Install piggyback cartridge"); Log_print("\t-cart2-type Set piggyback cartridge type (0..%i)", CARTRIDGE_LAST_SUPPORTED); Log_print("\t-cart-autoreboot Reboot when cartridge is inserted/removed"); Log_print("\t-no-cart-autoreboot Don't reboot after changing cartridge"); } argv[j++] = argv[i]; } if (a_m) { Log_print("Missing argument for '%s'", argv[i]); return FALSE; } else if (a_i) { Log_print("Invalid argument for '%s'", argv[--i]); return FALSE; } } *argc = j; if (help_only) return TRUE; /* If filename not given, we must reset the cartridge types. */ if (CARTRIDGE_main.filename[0] == '\0') CARTRIDGE_main.type = CARTRIDGE_NONE; if (CARTRIDGE_piggyback.filename[0] == '\0') CARTRIDGE_piggyback.type = CARTRIDGE_NONE; InitInsert(&CARTRIDGE_main); if (CartIsPassthrough(CARTRIDGE_main.type)) InitInsert(&CARTRIDGE_piggyback); return TRUE; } void CARTRIDGE_Exit(void) { CARTRIDGE_Remove(); /* Removes both cartridges */ } #ifndef BASIC void 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 */ StateSav_ReadINT(&saved_type, 1); if (saved_type != CARTRIDGE_NONE) { StateSav_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.). */ StateSav_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; StateSav_ReadINT(&saved_type, 1); StateSav_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.). */ StateSav_ReadINT(&CARTRIDGE_piggyback.state, 1); else { /* Savestate version 6 explicitely stored information about the active cartridge. */ int piggyback_active; StateSav_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(); } 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); } } #endif /* vim:ts=4:sw=4: */