// ======================================================================== // // LOCUTUS -- aka. LTO Flash! // // // // This class models Locutus, and serves as a structured container for // // a Locutus compatible cartridge. This may be initialized by or // // serialized to a LUIGI file. // // // // Note: This is not tightly integrated into jzIntv at all. Rather, // // some adaptor C code will bridge between this C++ and jzIntv. // // ======================================================================== // #include "locutus.hpp" #include #include #include #define LOC_JLP_SG_OFS (224) // Arbitrary; however must be mult of 8 using namespace std; extern "C" { extern int jlp_accel_on; extern int lto_isa_enabled; } // ------------------------------------------------------------------------ // // Constructor // // ------------------------------------------------------------------------ // t_locutus::t_locutus() : uid ( 0 ) , cpu_cache ( nullptr ) , jlp_savegame ( ) , jlp_sg_start ( 0 ) , jlp_sg_end ( 0 ) , jlp_sg_addr ( 0 ) , jlp_sg_row ( 0 ) , jlp_sg_file ( nullptr ) , jlp_crc16 ( 0 ) , jlp_xregs ( nullptr ) , jlp_sleep ( 0 ) , jlp_rand ( 0x4A5A6A7A ) , is_scrambled ( false ) { memset( static_cast(&ram [0] ), 0, sizeof( ram ) ); memset( static_cast(&mem_map [0][0]), 0, sizeof( mem_map ) ); memset( static_cast(&pfl_map [0][0]), 0, sizeof( pfl_map ) ); memset( static_cast(druid ), 0, sizeof( druid ) ); for (int i = 0; i < 256; i++) mem_perm[i][0].reset(); for (int i = 0; i < 256; i++) mem_perm[i][1].reset(); for (int i = 0; i < 16; i++) for (int j = 0; j < 16; j++) pfl_perm[i][j].reset(); for (int i = 0; i < 16; i++) bsel[i] = 0; for (int i = 0; i < 16; i++) jlp_regs[i] = 0; initialized.reset(); } // ------------------------------------------------------------------------ // // GET_INITIALIZED_SPAN_LIST Get the spans of initialized memory // // ------------------------------------------------------------------------ // t_addr_list t_locutus::get_initialized_span_list( void ) const { uint32_t s_addr = 0, addr; bool in_span = false; t_addr_list init_spans; for (addr = 0; addr < 512 * 1024; addr++) { if ( !in_span && initialized[addr] ) { s_addr = addr; in_span = true; } else if ( in_span && !initialized[addr] ) { init_spans.push_back( t_addr_span( s_addr, addr - 1 ) ); in_span = false; } } if (in_span) init_spans.push_back( t_addr_span( s_addr, addr - 1 ) ); return init_spans; } // ======================================================================== // // Intellivision Interface! // // ======================================================================== // // ------------------------------------------------------------------------ // // INTV_READ // // ------------------------------------------------------------------------ // uint16_t t_locutus::intv_read( const uint16_t intv_addr, const bool force ) const { jlp_rand.update( intv_addr, 16 ); if ( jlp_sleep > 0 ) { jlp_sleep--; return 0xFFFF; } if ( ( intv_addr <= 0x04FF ) || ( intv_addr >= 0x1000 && intv_addr <= 0x1FFF ) || ( intv_addr >= 0x3000 && intv_addr <= 0x47FF ) ) return 0xFFFF; const uint8_t para = intv_addr >> 8; const t_perm perm = get_mem_perm( para, false ); if ( jlp_accel_on && ( ( intv_addr >= 0x8000 && intv_addr <= 0x803F ) || ( intv_addr >= 0x9F80 && intv_addr <= 0x9FFF ) ) ) return do_jlp_accel_read( intv_addr ); if ( force || perm[LOCUTUS_PERM_READ] ) { const uint32_t map = get_mem_map( para, false ); const uint32_t locu_addr = ( map << 8 ) | ( intv_addr & 0xFF ); return read( locu_addr ); } else { return 0xFFFF; } } // ------------------------------------------------------------------------ // // DO_BANKSW_WRITE // // ------------------------------------------------------------------------ // void t_locutus::do_banksw_write( const uint16_t addr, const uint16_t data ) { const uint16_t hchap_addr = ((addr & 0xF) << 12) | ((addr & 0x10) << 7); const uint8_t para = hchap_addr >> 8; if ( !get_mem_perm( para, false )[LOCUTUS_PERM_BANKSW] ) return; // Intellicart bank switching: // // Each register accepts an 8-bit value. (The upper 8 bits of the // value written are ignored.) The lower 8 bits of the value written // specify the upper 8 bits of the target address for that 2K range. // The target address is combined with the CPU address in this manner: // // target_addr = bank_select << 8; // icart_addr = (intv_addr & 0x07FF) + target_addr; for (int i = 0; i < 8; i++) { set_mem_map( para + i, false, (data + i) & 0xFF ); } if (cpu_cache) { const uint16_t cpu_addr = para << 8; cpu_cache->invalidate( cpu_addr, cpu_addr + 2047 ); } } // ------------------------------------------------------------------------ // // DO_PAGEFLIP_WRITE // // ------------------------------------------------------------------------ // void t_locutus::do_pageflip_write( const uint16_t intv_addr, const uint16_t data ) { const uint8_t chap = intv_addr >> 12; const uint8_t page = data & 0xF; if ( chap == 0 ) return; if ( !get_pageflip_perm( chap, page )[LOCUTUS_PERM_BANKSW] ) return; t_perm perm = get_pageflip_perm( chap, page ); const uint16_t map = get_pageflip_map ( chap, page ) << 4; const uint8_t para = chap << 4; perm[LOCUTUS_PERM_BANKSW] = false; bsel[chap] = page; if ( chap != 4 ) { for (int i = 0; i < 16; i++) { set_mem_map ( para + i, false, map + i ); set_mem_perm( para + i, false, perm ); } if (cpu_cache) cpu_cache->invalidate( para << 8, (para << 8) + 4095 ); } else { // on $4xxx, only flip $4800 - $4FFF for (int i = 8; i < 16; i++) { set_mem_map ( para + i, false, map + i ); set_mem_perm( para + i, false, perm ); } if (cpu_cache) cpu_cache->invalidate( 0x4800, 0x4FFF ); } } // ------------------------------------------------------------------------ // // INTV_WRITE // // ------------------------------------------------------------------------ // void t_locutus::intv_write( const uint16_t intv_addr, const uint16_t data ) { jlp_rand.update( data, 16 ); jlp_rand.update( intv_addr, 16 ); if ( jlp_sleep > 0 ) { jlp_sleep--; return; } if ( intv_addr >= 0x40 && intv_addr <= 0x5F) { do_banksw_write( intv_addr, data ); return; } const uint8_t our_para = intv_addr >> 8; const t_perm our_perm = get_mem_perm( our_para, false ); // Handle PV register updates if ( intv_addr != 0x9F8D && (our_perm[LOCUTUS_PERM_READ] || our_perm[LOCUTUS_PERM_WRITE]) ) { do_jlp_accel_write( 0x9F8D, intv_read( intv_addr, true ) ); } // Intercept JLP accelerator writes even before page-flip writes if ( jlp_accel_on && ( ( intv_addr >= 0x8000 && intv_addr < 0x8040 ) || ( intv_addr >= 0x9F80 && intv_addr < 0x9FFF ) ) ) { do_jlp_accel_write( intv_addr, data ); return; } // That includes the switch to turn the accelerators on if ( !jlp_accel_on && intv_addr == 0x8033 && data == 0x4A5A && get_jlp_accel() != JLP_DISABLED ) { flip_jlp_accel( true ); return; } // Always handle page-flip writes before anything else (except JLP) if ( ( intv_addr & 0x0FFF) == 0xFFF && ((intv_addr & 0xF000) | 0x0A50 ) == ( data & 0xFFF0 ) && (!jlp_accel_on || (intv_addr != 0x8FFF && intv_addr != 0x9FFF)) ) { do_pageflip_write( intv_addr, data ); return; } // Feature flag 32 controls whether we respond to Locutus mapper writes // in the $1000 - $14FF address range. If it's not set, we ignore the // write. This should be controlled by the lto_mapper variable in the // .CFG file used to create the LUIGI. // // Also, ignore the write if the user marked the segment writeable. if ( feature_flag[32] && !our_perm[LOCUTUS_PERM_WRITE] ) { const bool reset_copy = intv_addr & 0x100; // Locutus native memory mapping update if ( intv_addr >= 0x1000 && intv_addr <= 0x11FF ) { uint8_t upd_para = intv_addr & 0xFF; set_mem_map( upd_para, reset_copy, data ); return; } // Locutus native memory permission update if ( intv_addr >= 0x1200 && intv_addr <= 0x13FF ) { uint8_t upd_para = intv_addr & 0xFF; t_perm new_perm; new_perm[0] = data & (1 << 0); new_perm[1] = data & (1 << 1); new_perm[2] = data & (1 << 2); new_perm[3] = data & (1 << 3); set_mem_perm( upd_para, reset_copy, new_perm ); return; } // Locutus native page-flip table update if ( intv_addr >= 0x1400 && intv_addr <= 0x14FF ) { const int i = (intv_addr >> 4) & 0xF; const int j = (intv_addr >> 0) & 0xF; const uint16_t m = (data & 0x07F0) >> 4; // memory map t_perm new_perm; new_perm[0] = data & (1 << 0); new_perm[1] = data & (1 << 1); new_perm[2] = data & (1 << 2); new_perm[3] = data & (1 << 3); set_pageflip_map ( i, j, m ); set_pageflip_perm( i, j, new_perm ); return; } } // Normal data write! Who'd have thunk it? if ( our_perm[LOCUTUS_PERM_WRITE] ) { const uint32_t map = get_mem_map( our_para, false ); const uint32_t locu_addr = ( map << 8 ) | (intv_addr & 0xFF); write( locu_addr, data ); } } // ------------------------------------------------------------------------ // // INTV_RESET // // ------------------------------------------------------------------------ // void t_locutus::intv_reset( void ) { for (int para = 0; para < 256; para++) set_mem_map ( para, false, get_mem_map( para, true ) ); for (int para = 0; para < 256; para++) set_mem_perm( para, false, get_mem_perm( para, true ) ); for (int chap = 0; chap < 16; chap++) bsel[chap] = 0; const auto jlp_accel = get_jlp_accel(); jlp_accel_on = false; if ( jlp_accel == JLP_ACCEL_ON || jlp_accel == JLP_ACCEL_FLASH_ON ) flip_jlp_accel( true ); // Note: Do *nothing* if JLP_DISABLED or JLP_ACCEL_OFF const auto lto_mapper = get_enable_lto_mapper(); lto_isa_enabled = lto_mapper || jlp_accel != JLP_DISABLED; } // ======================================================================== // // JLP Acceleration Support // // ======================================================================== // // ------------------------------------------------------------------------ // // INIT_JLP_FLASH Initialize JLP Flash support. // // ------------------------------------------------------------------------ // void t_locutus::init_jlp_flash( const char* jlp_savegame_ ) { // Close any open save-game file image if ( jlp_sg_file ) { fclose( jlp_sg_file ); jlp_sg_file = nullptr; } // Get JLP flash parameters const auto jlp_accel = get_jlp_accel(); const auto jlp_flash = jlp_accel == JLP_DISABLED ? 0u : get_jlp_flash(); // If flash is disabled, set it up that way. if ( !jlp_flash ) { jlp_sg_start = 0; jlp_sg_end = 0; jlp_sg_bytes = 0; jlp_sg_img.resize(0); return; } // Otherwise, initialize the JLP flash image jlp_sg_start = LOC_JLP_SG_OFS; jlp_sg_end = LOC_JLP_SG_OFS + jlp_flash * 8 - 1; jlp_sg_bytes = 192 * 8 * jlp_flash; jlp_sg_addr = 0xBADDu; jlp_sg_row = 0xBADDu; jlp_sg_img.resize( jlp_sg_bytes, 0xFF ); // If we were given a backing file, open it. if ( jlp_savegame_ ) jlp_savegame = jlp_savegame_; else jlp_savegame.clear(); const char *fname = jlp_savegame_; if ( fname && ( (jlp_sg_file = fopen(fname, "r+b")) != nullptr || (jlp_sg_file = fopen(fname, "w+b")) != nullptr ) ) { // Try to read in the file image fseek( jlp_sg_file, 0, SEEK_SET ); if ( fread( &(jlp_sg_img[0]), 1, jlp_sg_bytes, jlp_sg_file) != jlp_sg_bytes ) { // ignore errors here for now; they're OK } // Create a backup file if we can std::string jlp_savegame_bak = jlp_savegame + "~"; const char *fname_bak = jlp_savegame_bak.c_str(); FILE *f_bak = fopen( fname_bak, "wb" ); if (f_bak) { fwrite( &(jlp_sg_img[0]), 1, jlp_sg_bytes, f_bak ); fclose( f_bak ); } // Write our initial image to the backing file as part of init fseek( jlp_sg_file, 0, SEEK_SET ); if ( fwrite( &(jlp_sg_img[0]), 1, jlp_sg_bytes, jlp_sg_file ) != jlp_sg_bytes ) { throw std::string( "Locutus JLP: Unable to initialize backing file\n"); } fflush( jlp_sg_file ); } } // ------------------------------------------------------------------------ // // DO_JLP_ACCEL_READ // // ------------------------------------------------------------------------ // uint16_t t_locutus::do_jlp_accel_read( const uint16_t addr ) const { if ( addr >= 0x8000 && addr < 0x8023 ) return 0; if ( addr == 0x8023 ) return jlp_sg_start; if ( addr == 0x8024 ) return jlp_sg_end; if ( addr >= 0x8025 && addr < 0x8040 ) return 0; if ( addr >= 0x9F80 && addr < 0x9F90 ) return jlp_regs[ addr & 0xF ]; if ( addr >= 0x9F90 && addr < 0x9FA0 && jlp_xregs ) return jlp_xregs[ addr & 0xF ]; if ( addr >= 0x9FA0 && addr < 0x9FFD ) return 0xFFFF; if ( addr == 0x9FFD ) return jlp_crc16.get(); if ( addr == 0x9FFE ) return next_rand() & 0xFFFF; if ( addr == 0x9FFF ) return 0; return 0xFFFF; } // ------------------------------------------------------------------------ // // DO_JLP_ACCEL_WRITE // // ------------------------------------------------------------------------ // void t_locutus::do_jlp_accel_write( const uint16_t addr, const uint16_t data ) { // -------------------------------------------------------------------- // // Check for mult/div writes // // $9F8(0,1): s16($9F80) x s16($9F81) -> s32($9F8F:$9F8E) // // $9F8(2,3): s16($9F82) x u16($9F83) -> s32($9F8F:$9F8E) // // $9F8(4,5): u16($9F84) x s16($9F85) -> s32($9F8F:$9F8E) // // $9F8(6,7): u16($9F86) x u16($9F87) -> u32($9F8F:$9F8E) // // $9F8(8,9): s16($9F88) / s16($9F89) -> quot($9F8E), rem($9F8F) // // $9F8(A,B): u16($9F8A) / u16($9F8B) -> quot($9F8E), rem($9F8F) // // -------------------------------------------------------------------- // if ( addr >= 0x9F80 && addr <= 0x9F8F ) { uint32_t prod, quot, rem; jlp_regs[ addr & 0xF ] = data; switch (addr & 0xF) { case 0: case 1: prod = uint32_t( int32_t( int16_t( jlp_regs[0] ) ) * int32_t( int16_t( jlp_regs[1] ) ) ); jlp_regs[0xE] = prod; jlp_regs[0xF] = prod >> 16; break; case 2: case 3: prod = uint32_t( int32_t( int16_t( jlp_regs[2] ) ) * int32_t( uint16_t( jlp_regs[3] ) ) ); jlp_regs[0xE] = prod; jlp_regs[0xF] = prod >> 16; break; case 4: case 5: prod = uint32_t( int32_t( uint16_t( jlp_regs[4] ) ) * int32_t( int16_t( jlp_regs[5] ) ) ); jlp_regs[0xE] = prod; jlp_regs[0xF] = prod >> 16; break; case 6: case 7: prod = uint32_t( uint32_t( uint16_t( jlp_regs[6] ) ) * uint32_t( uint16_t( jlp_regs[7] ) ) ); jlp_regs[0xE] = prod; jlp_regs[0xF] = prod >> 16; break; case 8: case 9: if ( jlp_regs[9] ) { quot = uint32_t( int16_t( jlp_regs[8] ) / int16_t( jlp_regs[9] ) ); rem = uint32_t( int16_t( jlp_regs[8] ) % int16_t( jlp_regs[9] ) ); jlp_regs[0xE] = quot; jlp_regs[0xF] = rem; } break; case 10: case 11: if ( jlp_regs[11] ) { quot = uint32_t( uint16_t( jlp_regs[10] ) / uint16_t( jlp_regs[11] ) ); rem = uint32_t( uint16_t( jlp_regs[10] ) % uint16_t( jlp_regs[11] ) ); jlp_regs[0xE] = quot; jlp_regs[0xF] = rem; } break; } return; } // -------------------------------------------------------------------- // // Check for JLP X-Regs // // -------------------------------------------------------------------- // if ( addr >= 0x9F90 && addr <= 0x9F9F && jlp_xregs ) { jlp_xregs[ addr & 0xF ] = data; return; } // -------------------------------------------------------------------- // // Check for Save-Game Arch V2 writes // // // // $8025 JLP RAM address to operate on // // $8026 Flash row to operate on // // // // $802D Copy JLP RAM to flash row (unlock: $C0DE) // // $802E Copy JLP RAM to flash row (unlock: $DEC0) // // $802F Copy JLP RAM to flash row (unlock: $BEEF) // // -------------------------------------------------------------------- // if ( addr == 0x8025 ) { jlp_sg_addr = data; return; } if ( addr == 0x8026 ) { jlp_sg_row = data; return; } if ( addr == 0x802D && data == 0xC0DE ) { jlp_sg_ram_to_flash(); return; } if ( addr == 0x802E && data == 0xDEC0 ) { jlp_sg_flash_to_ram(); return; } if ( addr == 0x802F && data == 0xBEEF ) { jlp_sg_erase_sector(); return; } // -------------------------------------------------------------------- // // Check for CRC-16 accelerator writes // // -------------------------------------------------------------------- // if ( addr == 0x9FFC ) jlp_crc16.update( data, 16 ); if ( addr == 0x9FFD ) jlp_crc16.set( data ); // -------------------------------------------------------------------- // // Check for JLP Accelerator Disable // // -------------------------------------------------------------------- // if ( addr == 0x8034 && data == 0x6A7A ) flip_jlp_accel( false ); } // ------------------------------------------------------------------------ // // JLP_SG_VALIDATE_ARGS Ensure JLP SG command arguments are valid // // ------------------------------------------------------------------------ // bool t_locutus::jlp_sg_validate_args( const bool chk_addr ) const { if ( jlp_sg_row < jlp_sg_start || jlp_sg_row > jlp_sg_end ) return false; if ( chk_addr && (jlp_sg_addr < 0x8040 || jlp_sg_addr + 95 > 0x9F7F) ) return false; return true; } // ------------------------------------------------------------------------ // // JLP_SG_RAM_TO_FLASH Copy RAM to a row in flash // // ------------------------------------------------------------------------ // void t_locutus::jlp_sg_ram_to_flash( void ) { jlp_sleep = 400 + next_rand() % 200; if ( !jlp_sg_validate_args() ) return; const uint32_t row_idx = (jlp_sg_row - jlp_sg_start) * 192; // Refuse to write if row isn't empty for ( int i = 0; i < 192; i++ ) if ( jlp_sg_img[ row_idx + i ] != 0xFF ) return; // Copy the data from RAM to flash in little-endian for ( int i = 0, a = 0; i < 192; i += 2, a++ ) { const uint16_t intv_addr = jlp_sg_addr + a; const uint16_t para = intv_addr >> 8; const uint32_t map = get_mem_map( para, false ); const uint32_t locu_addr = ( map << 8 ) | ( intv_addr & 0xFF ); const uint16_t data = read( locu_addr ); jlp_sg_img[ row_idx + i + 0 ] = data & 0xFF; jlp_sg_img[ row_idx + i + 1 ] = data >> 8; } // Update backing file if ( jlp_sg_file ) { fseek( jlp_sg_file, row_idx, SEEK_SET ); fwrite( &(jlp_sg_img[row_idx]), 2, 96, jlp_sg_file ); fflush( jlp_sg_file ); } } // ------------------------------------------------------------------------ // // JLP_SG_FLASH_TO_RAM Copy a row in flash to RAM // // ------------------------------------------------------------------------ // void t_locutus::jlp_sg_flash_to_ram( void ) { jlp_sleep = 10 + next_rand() % 20; if ( !jlp_sg_validate_args() ) return; const uint32_t row_idx = (jlp_sg_row - jlp_sg_start) * 192; // Copy the data from flash to RAM in little-endian for ( int i = 0, a = 0; i < 192; i += 2, a++ ) { const uint16_t intv_addr = jlp_sg_addr + a; const uint16_t para = intv_addr >> 8; const uint32_t map = get_mem_map( para, false ); const uint32_t locu_addr = ( map << 8 ) | ( intv_addr & 0xFF ); const uint16_t data_lo = jlp_sg_img[ row_idx + i + 0 ]; const uint16_t data_hi = jlp_sg_img[ row_idx + i + 1 ]; const uint16_t data = data_lo | (data_hi << 8); write( locu_addr, data ); } } // ------------------------------------------------------------------------ // // JLP_SG_ERASE_SECTOR Erase a sector (8 rows) // // ------------------------------------------------------------------------ // void t_locutus::jlp_sg_erase_sector( void ) { jlp_sleep = 800 + next_rand() % 200; if ( !jlp_sg_validate_args( false ) ) return; const uint32_t row_idx = ((jlp_sg_row - jlp_sg_start) & ~7) * 192; memset( &(jlp_sg_img[row_idx]), 0xFF, 192 * 8 ); // Update backing file if ( jlp_sg_file ) { fseek( jlp_sg_file, row_idx, SEEK_SET ); fwrite( &(jlp_sg_img[row_idx]), 2, 96 * 8, jlp_sg_file ); fflush( jlp_sg_file ); } } // ------------------------------------------------------------------------ // // FLIP_JLP_ACCEL Flip the JLP Accelerators on/off. // // ------------------------------------------------------------------------ // void t_locutus::flip_jlp_accel( const bool new_jlp_accel_on ) { // printf("Locutus: flip_jlp_accel %s\n", new_jlp_accel_on ? "ON" : "off"); // fflush(stdout); // Locutus ignores attempts to switch into the already-active mode. // This is important, as updates to the various memory mapper structures // that happened since the last flip will be retained. if ( new_jlp_accel_on == !!jlp_accel_on ) return; jlp_accel_on = new_jlp_accel_on; // Turning JLP Accelerators + RAM on: // // -- Map $8040 - $9F7F through to Locutus $10040 - $11F7F // -- Mark $8040 - $9F7F +R, +W, 16-bit, no page flip, no bankswitch // -- JLP Accelerators become visible at $8000 - $803F, $9F80 - $9FFF // // Note that we don't actually modify the flip table, and we do remember // which page was last flipped in on $8xxx and $9xxx. if ( jlp_accel_on ) { const t_perm perm = 0x3; // +R, +W, 16-bit, no pageflip/bankswitch uint16_t jlp_map = 0x0100; for ( uint8_t para = 0x80 ; para <= 0x9F ; para++ ) { set_mem_perm( para, false, perm ); set_mem_map ( para, false, jlp_map++ ); } } else // Turning JLP Accelerators + RAM off: // // -- $8000 - $9FFF get mapped to most recently flipped-in page // -- JLP Accelerators become invisible { for ( uint8_t chap = 8; chap <= 9; chap++ ) { const uint8_t page = bsel[chap]; t_perm perm = get_pageflip_perm( chap, page ); const uint16_t map = get_pageflip_map ( chap, page ) << 4; const uint8_t para = chap << 4; perm[LOCUTUS_PERM_BANKSW] = false; for (int i = 0; i < 16; i++) { set_mem_map ( para + i, false, map + i ); set_mem_perm( para + i, false, perm ); } } } if (cpu_cache) cpu_cache->invalidate( 0x8000, 0x9FFF ); } // ------------------------------------------------------------------------ // // Feature Flags // // // // Bit(s) Description // // ------- ----------------------------------------------------------- // // 0..1 Intellivoice compatibility // // 2..3 ECS compatibility // // 4..5 Intellivision 2 compatibility // // 6..7 Keyboard Component compatibility // // 8..15 Reserved // // 16..17 JLP Accelerator Enable (MPY/DIV/etc at $9F80 - $9FFF) // // 18..21 Reserved // // 22..31 JLP Flash Save-Game minimum required size in sectors // // 32 LTO Mapper // // 33..62 Reserved // // 63 Defaults flag: 0 = defaults, 1 = explicitly set by user // // 64..127 Reserved. // // // // Note: t_metadata and feature_flags must stay in sync. If an outside // // class directly modifies t_metadata or the feature flags, it must take // // an additional step to bring them back in sync. // // // // The getters will assert() if they are out of sync at a get(). // // ------------------------------------------------------------------------ // void t_locutus::set_compat_voice( const compat_level_t voice_compat ) { const int voice = int( voice_compat ); set_feature_flag( 0, voice & 1 ); set_feature_flag( 1, voice & 2 ); metadata.voice_compat = voice_compat; } void t_locutus::set_compat_ecs ( const compat_level_t ecs_compat ) { const int ecs = int( ecs_compat ); set_feature_flag( 2, ecs & 1 ); set_feature_flag( 3, ecs & 2 ); metadata.ecs_compat = ecs_compat; } void t_locutus::set_compat_intv2( const compat_level_t intv2_compat ) { const int intv2 = int( intv2_compat ); set_feature_flag( 4, intv2 & 1 ); set_feature_flag( 5, intv2 & 2 ); metadata.intv2_compat = intv2_compat; } void t_locutus::set_compat_kc( const compat_level_t kc_compat ) { const int kc = int( kc_compat ); set_feature_flag( 6, kc & 1 ); set_feature_flag( 7, kc & 2 ); metadata.kc_compat = kc_compat; } void t_locutus::set_compat_tv( const compat_level_t tv_compat ) { const int tv = int( tv_compat ); if ( tv_compat == CMP_TOLERATES ) { set_feature_flag( 8, 0 ); set_feature_flag( 9, 0 ); set_feature_flag( 10, 0 ); set_feature_flag( 11, 0 ); } else { set_feature_flag( 8, 1 ); set_feature_flag( 9, 0 ); set_feature_flag( 10, tv & 1 ); set_feature_flag( 11, tv & 2 ); } metadata.tv_compat = tv_compat; } void t_locutus::set_jlp_features( const jlp_accel_t jlp_accel, const unsigned jlp_flash ) { const int jlp_flag = int( jlp_accel ); set_feature_flag( 16, jlp_flag & 1 ); set_feature_flag( 17, jlp_flag & 2 ); // Note: jlp_flash ignored if (jlp_accel & 2) == 0 set_feature_flag( 22, ( jlp_flash >> 0 ) & 1 ); set_feature_flag( 23, ( jlp_flash >> 1 ) & 1 ); set_feature_flag( 24, ( jlp_flash >> 2 ) & 1 ); set_feature_flag( 25, ( jlp_flash >> 3 ) & 1 ); set_feature_flag( 26, ( jlp_flash >> 4 ) & 1 ); set_feature_flag( 27, ( jlp_flash >> 5 ) & 1 ); set_feature_flag( 28, ( jlp_flash >> 6 ) & 1 ); set_feature_flag( 29, ( jlp_flash >> 7 ) & 1 ); set_feature_flag( 30, ( jlp_flash >> 8 ) & 1 ); set_feature_flag( 31, ( jlp_flash >> 9 ) & 1 ); metadata.jlp_accel = jlp_accel; metadata.jlp_flash = jlp_flash; } void t_locutus::set_enable_lto_mapper( const bool lto_mapper ) { set_feature_flag( 32, lto_mapper ? 1 : 0 ); metadata.lto_mapper = lto_mapper; } void t_locutus::set_explicit_flags( const bool explicit_flags ) { set_feature_flag( 63, explicit_flags ? 1 : 0 ); metadata.is_defaults = !explicit_flags; } compat_level_t t_locutus::get_compat_voice( void ) const { int voice = ( get_feature_flag( 0 ) ? 1 : 0 ) | ( get_feature_flag( 1 ) ? 2 : 0 ); compat_level_t voice_compat = compat_level_t( voice ); assert( voice_compat == metadata.voice_compat ); return voice_compat; } compat_level_t t_locutus::get_compat_ecs ( void ) const { int ecs = ( get_feature_flag( 2 ) ? 1 : 0 ) | ( get_feature_flag( 3 ) ? 2 : 0 ); compat_level_t ecs_compat = compat_level_t( ecs ); assert( ecs_compat == metadata.ecs_compat ); return ecs_compat; } compat_level_t t_locutus::get_compat_intv2( void ) const { int intv2 = ( get_feature_flag( 4 ) ? 1 : 0 ) | ( get_feature_flag( 5 ) ? 2 : 0 ); compat_level_t intv2_compat = compat_level_t( intv2 ); assert( intv2_compat == metadata.intv2_compat ); return intv2_compat; } compat_level_t t_locutus::get_compat_kc ( void ) const { int kc = ( get_feature_flag( 6 ) ? 1 : 0 ) | ( get_feature_flag( 7 ) ? 2 : 0 ); compat_level_t kc_compat = compat_level_t( kc ); assert( kc_compat == metadata.kc_compat ); return kc_compat; } compat_level_t t_locutus::get_compat_tv ( void ) const { int ex = ( get_feature_flag( 8 ) ? 1 : 0 ) | ( get_feature_flag( 9 ) ? 2 : 0 ); int tv = ( get_feature_flag( 10 ) ? 1 : 0 ) | ( get_feature_flag( 11 ) ? 2 : 0 ); compat_level_t tv_compat = ex ? compat_level_t( tv ) : CMP_TOLERATES; assert( tv_compat == metadata.tv_compat ); return tv_compat; } jlp_accel_t t_locutus::get_jlp_accel( void ) const { int jlp_accel_val = ( get_feature_flag( 16 ) ? 1 : 0 ) | ( get_feature_flag( 17 ) ? 2 : 0 ); jlp_accel_t jlp_accel = jlp_accel_t( jlp_accel_val ); assert( jlp_accel == metadata.jlp_accel ); return jlp_accel; } unsigned t_locutus::get_jlp_flash( void ) const { // Note: jlp_flash ignored if (jlp_accel & 2) == 0 int jlp_flash = ( get_feature_flag( 22 ) ? 1 : 0 ) | ( get_feature_flag( 23 ) ? 2 : 0 ) | ( get_feature_flag( 24 ) ? 4 : 0 ) | ( get_feature_flag( 25 ) ? 8 : 0 ) | ( get_feature_flag( 26 ) ? 16 : 0 ) | ( get_feature_flag( 27 ) ? 32 : 0 ) | ( get_feature_flag( 28 ) ? 64 : 0 ) | ( get_feature_flag( 29 ) ? 128 : 0 ) | ( get_feature_flag( 30 ) ? 256 : 0 ) | ( get_feature_flag( 31 ) ? 512 : 0 ); assert( jlp_flash == metadata.jlp_flash ); return jlp_flash; } bool t_locutus::get_enable_lto_mapper( void ) const { bool lto_mapper = get_feature_flag( 32 ); assert( lto_mapper == metadata.lto_mapper ); return lto_mapper; } bool t_locutus::get_explicit_flags( void ) const { return get_feature_flag( 63 ); } void t_locutus::copy_feature_flag_to_metadata( ) { int voice = ( get_feature_flag( 0 ) ? 1 : 0 ) | ( get_feature_flag( 1 ) ? 2 : 0 ); metadata.voice_compat = compat_level_t( voice ); int ecs = ( get_feature_flag( 2 ) ? 1 : 0 ) | ( get_feature_flag( 3 ) ? 2 : 0 ); metadata.ecs_compat = compat_level_t( ecs ); int intv2 = ( get_feature_flag( 4 ) ? 1 : 0 ) | ( get_feature_flag( 5 ) ? 2 : 0 ); metadata.intv2_compat = compat_level_t( intv2 ); int kc = ( get_feature_flag( 6 ) ? 1 : 0 ) | ( get_feature_flag( 7 ) ? 2 : 0 ); metadata.kc_compat = compat_level_t( kc ); int ex = ( get_feature_flag( 8 ) ? 1 : 0 ) | ( get_feature_flag( 9 ) ? 2 : 0 ); int tv = ( get_feature_flag( 10 ) ? 1 : 0 ) | ( get_feature_flag( 11 ) ? 2 : 0 ); metadata.tv_compat = ex ? compat_level_t( tv ) : CMP_TOLERATES; int jlp_accel_val = ( get_feature_flag( 16 ) ? 1 : 0 ) | ( get_feature_flag( 17 ) ? 2 : 0 ); metadata.jlp_accel = jlp_accel_t( jlp_accel_val ); // Note: jlp_flash ignored if (jlp_accel & 2) == 0 int jlp_flash = ( get_feature_flag( 22 ) ? 1 : 0 ) | ( get_feature_flag( 23 ) ? 2 : 0 ) | ( get_feature_flag( 24 ) ? 4 : 0 ) | ( get_feature_flag( 25 ) ? 8 : 0 ) | ( get_feature_flag( 26 ) ? 16 : 0 ) | ( get_feature_flag( 27 ) ? 32 : 0 ) | ( get_feature_flag( 28 ) ? 64 : 0 ) | ( get_feature_flag( 29 ) ? 128 : 0 ) | ( get_feature_flag( 30 ) ? 256 : 0 ) | ( get_feature_flag( 31 ) ? 512 : 0 ); metadata.jlp_flash = jlp_flash; metadata.lto_mapper = get_feature_flag( 32 ); metadata.is_defaults = !get_explicit_flags(); } // ------------------------------------------------------------------------ // // T_CPU_CACHE_IF::~T_CPU_CACHE_IF // // Anchor this class' VTable! // // ------------------------------------------------------------------------ // t_cpu_cache_if::~t_cpu_cache_if() { // Nothing. }