1 // ======================================================================== //
2 //  LOCUTUS -- aka. LTO Flash!                                              //
3 //                                                                          //
4 //  This class models Locutus, and serves as a structured container for     //
5 //  a Locutus compatible cartridge.  This may be initialized by or          //
6 //  serialized to a LUIGI file.                                             //
7 //                                                                          //
8 //  Note:  This is not tightly integrated into jzIntv at all.  Rather,      //
9 //  some adaptor C code will bridge between this C++ and jzIntv.            //
10 //                                                                          //
11 //  Terms:                                                                  //
12 //   -- byte:       8 bits                                                  //
13 //   -- word:       16 bits                                                 //
14 //   -- para:       256 words (para is short for "paragraph")               //
15 //   -- page:       4K words (16 para)                                      //
16 //   -- chap:       A group of pages that map to the same 4K address span   //
17 //                                                                          //
18 // ======================================================================== //
19 #ifndef LOCUTUS_HPP_
20 #define LOCUTUS_HPP_
21 
22 #include "locutus_types.hpp"
23 #include "crc_calc.hpp"
24 #include <algorithm>
25 #include <iterator>
26 #include <string>
27 
28 static const int LOCUTUS_FEAT_JLP_ACCEL     = 0;
29 static const int LOCUTUS_FEAT_JLP_SAVEGAME  = 1;
30 
31 class t_cpu_cache_if                // interface back to the CPU decode cache
32 {
33     public:
34         virtual void invalidate( uint16_t addr_lo, uint16_t addr_hi ) = 0;
35         virtual ~t_cpu_cache_if();
36 };
37 
38 static const uint32_t LOCUTUS_CAPACITY = 512 * 1024;
39 
40 class t_locutus
41 {
42   private:
43     uint16_t        ram     [512 * 1024];
44     uint16_t        mem_map [256][2];   // bits 10:0 form bits 18:8 of addr
45     t_perm          mem_perm[256][2];
46     uint16_t        pfl_map [16][16];   // bits 6:0 form bits 18:12 of addr
47     t_perm          pfl_perm[16][16];
48     uint8_t         bsel    [16];       // Bank-select table (used by JLP only)
49     uint64_t        uid;
50 
51     t_cpu_cache_if* cpu_cache;
52     t_metadata      metadata;
53 
54     std::bitset<128>              feature_flag;
55     std::bitset<LOCUTUS_CAPACITY> initialized;
56 
57     std::string             jlp_savegame;       // JLP savegame filename
58     std::vector<uint8_t>    jlp_sg_img;         // JLP flash image
59     uint32_t                jlp_sg_bytes;
60     uint16_t                jlp_sg_start;
61     uint16_t                jlp_sg_end;
62     uint16_t                jlp_sg_addr;
63     uint16_t                jlp_sg_row;
64     FILE*                   jlp_sg_file;
65 
66     crc::jlp16              jlp_crc16;
67     uint16_t                jlp_regs[16];
68     uint16_t               *jlp_xregs;
69 
70     mutable uint16_t        jlp_sleep;
71     mutable crc::zip        jlp_rand;
72 
73     // These are set only if we try to load a scrambled LUIGI.
74     uint8_t druid[16];      // Scrambler DRUID
75     bool    is_scrambled;
76 
77     void do_banksw_write  ( uint16_t addr, uint16_t data );
78     void do_pageflip_write( uint16_t addr, uint16_t data );
79 
validate_addr(const char * loc,const uint32_t addr) const80     void validate_addr( const char* loc, const uint32_t addr ) const
81     {
82         if ( addr > LOCUTUS_CAPACITY )
83             throw loc + std::string(": Address out of range");
84     }
85 
86     // -------------------------------------------------------------------- //
87     //  Internal JLP Acceleration and JLP Flash support                     //
88     // -------------------------------------------------------------------- //
89     uint16_t do_jlp_accel_read ( const uint16_t addr ) const;
90     void     do_jlp_accel_write( const uint16_t addr, const uint16_t data );
91 
92     bool jlp_sg_validate_args( const bool chk_addr = true ) const;
93     void jlp_sg_ram_to_flash( void );
94     void jlp_sg_flash_to_ram( void );
95     void jlp_sg_erase_sector( void );
96 
97     void flip_jlp_accel( const bool new_jlp_accel_on );
98 
next_rand(void) const99     inline uint32_t next_rand( void ) const
100     {
101         jlp_rand.update( 0x4A5A6A7A, 32 );
102         return jlp_rand.get();
103     }
104 
105   public:
106 
107     t_locutus();
108 
109     // -------------------------------------------------------------------- //
110     //  INTV-facing interface                                               //
111     // -------------------------------------------------------------------- //
112     uint16_t intv_read ( const uint16_t addr, const bool force = false ) const;
113     void     intv_write( const uint16_t addr, const uint16_t data );
114     void     intv_reset( void );
115 
set_cpu_cache(t_cpu_cache_if * cpu_cache_)116     inline void set_cpu_cache ( t_cpu_cache_if* cpu_cache_ )
117     {
118         cpu_cache = cpu_cache_;
119     }
120 
set_xregs(uint16_t * x)121     inline void set_xregs( uint16_t *x )
122     {
123         jlp_xregs = x;
124     }
125 
126     // -------------------------------------------------------------------- //
127     //  JLP Acceleration and JLP Flash support interface                    //
128     // -------------------------------------------------------------------- //
129     void init_jlp_flash( const char* jlp_savegame_ );
130 
131     // -------------------------------------------------------------------- //
132     //  General purpose interface                                           //
133     //                                                                      //
134     //  For mem_map, mem_perm, the "reset" argument selects between the     //
135     //  currently active values (reset = false) or the values that will be  //
136     //  loaded at reset (reset = true).                                     //
137     // -------------------------------------------------------------------- //
read(const uint32_t addr) const138     inline uint16_t read( const uint32_t addr ) const
139     {
140         validate_addr( "t_locutus::read", addr );
141         return ram[addr];
142     }
143 
write(const uint32_t addr,const uint16_t data)144     inline void     write( const uint32_t addr, const uint16_t data )
145     {
146         validate_addr( "t_locutus::write", addr );
147         ram[addr] = data;
148         initialized[addr] = true;
149     }
150 
get_mem_map(const uint8_t para,const bool reset) const151     inline uint16_t get_mem_map ( const uint8_t para, const bool reset ) const
152     {
153         return mem_map[para][reset];
154     }
155 
set_mem_map(const uint8_t para,const bool reset,const uint16_t mapping)156     inline void     set_mem_map ( const uint8_t  para, const bool reset,
157                                   const uint16_t mapping )
158     {
159         mem_map[para][reset] = mapping & 0x07FF;
160     }
161 
get_mem_perm(const uint8_t para,const bool reset) const162     inline t_perm   get_mem_perm( const uint8_t  para, const bool reset ) const
163     {
164         return mem_perm[para][reset];
165     }
166 
set_mem_perm(const uint8_t para,const bool reset,const t_perm perm)167     inline void     set_mem_perm( const uint8_t  para, const bool reset,
168                                   const t_perm   perm )
169     {
170         mem_perm[para][reset] = perm;
171     }
172 
get_pageflip_map(const uint8_t chap,const uint8_t page) const173     inline uint16_t get_pageflip_map ( const uint8_t chap,
174                                        const uint8_t page ) const
175     {
176         return pfl_map[chap][page];
177     }
178 
get_pageflip_perm(const uint8_t chap,const uint8_t page) const179     inline t_perm   get_pageflip_perm( const uint8_t  chap,
180                                        const uint8_t  page ) const
181     {
182         return pfl_perm[chap][page];
183     }
184 
set_pageflip_map(const uint8_t chap,const uint8_t page,const uint16_t map)185     inline void     set_pageflip_map ( const uint8_t  chap,
186                                        const uint8_t  page,
187                                        const uint16_t map  )
188     {
189         pfl_map[chap][page] = map & 0x007F;
190     }
191 
set_pageflip_perm(const uint8_t chap,const uint8_t page,const t_perm perm)192     inline void     set_pageflip_perm( const uint8_t  chap,
193                                        const uint8_t  page,
194                                        const t_perm   perm )
195     {
196         pfl_perm[chap][page] = perm;
197     }
198 
set_pageflip(const uint8_t chap,const uint8_t page,const uint16_t map,const t_perm perm)199     inline void     set_pageflip     ( const uint8_t  chap,
200                                        const uint8_t  page,
201                                        const uint16_t map,
202                                        const t_perm   perm )
203     {
204         set_pageflip_map ( chap, page, map  );
205         set_pageflip_perm( chap, page, perm );
206     }
207 
get_initialized(const uint32_t addr) const208     inline bool     get_initialized  ( const uint32_t addr ) const
209     {
210         validate_addr( "t_locutus::get_initialized", addr );
211         return initialized[ addr ];
212     }
213 
set_initialized(const uint32_t addr,const bool ini)214     inline void     set_initialized  ( const uint32_t addr, const bool ini )
215     {
216         validate_addr( "t_locutus::set_initialized", addr );
217         initialized[ addr ] = ini;
218     }
219 
get_feature_flag(const int feat) const220     inline bool get_feature_flag( const int feat ) const
221     {
222         return feat < 128 ? feature_flag[feat] : false;
223     }
224 
set_feature_flag(const int feat,const bool value)225     inline void set_feature_flag( const int feat, const bool value )
226     {
227         if ( feat < 128 ) feature_flag[feat] = value;
228     }
229 
set_uid(const uint64_t uid_)230     inline void set_uid( const uint64_t uid_ )
231     {
232         uid = uid_;
233     }
234 
get_uid(void) const235     inline uint64_t get_uid( void ) const
236     {
237         return uid;
238     }
239 
240     t_addr_list get_initialized_span_list( void ) const;
241 
get_metadata(void)242     t_metadata&       get_metadata( void )       { return metadata; }
get_metadata(void) const243     const t_metadata& get_metadata( void ) const { return metadata; }
244 
set_metadata(const t_metadata & m)245     void set_metadata( const t_metadata& m )
246     {
247         metadata = m;
248     }
249 
was_scrambled() const250     bool was_scrambled() const
251     {
252         return is_scrambled;
253     }
254 
get_scramble_druid() const255     const uint8_t* get_scramble_druid() const
256     {
257         return is_scrambled ? druid : nullptr;
258     }
259 
260     // -------------------------------------------------------------------- //
261     //  Higher level feature-flag interface                                 //
262     // -------------------------------------------------------------------- //
263     void set_compat_ecs( const compat_level_t ecs_compat );
264     compat_level_t get_compat_ecs( void ) const;
265 
266     void set_compat_voice( const compat_level_t voice_compat );
267     compat_level_t get_compat_voice( void ) const;
268 
269     void set_compat_intv2( const compat_level_t intv2_compat );
270     compat_level_t get_compat_intv2( void ) const;
271 
272     void set_compat_kc( const compat_level_t kc_compat );
273     compat_level_t get_compat_kc( void ) const;
274 
275     void set_compat_tv( const compat_level_t tv_compat );
276     compat_level_t get_compat_tv( void ) const;
277 
278     void set_jlp_features( const jlp_accel_t jlp_accel,
279                            const unsigned jlp_flash );
280     jlp_accel_t get_jlp_accel( void ) const;
281     unsigned    get_jlp_flash( void ) const;
282 
283     void set_enable_lto_mapper( const bool lto_mapper );
284     bool get_enable_lto_mapper( void ) const;
285 
286     void set_explicit_flags( const bool explicit_flags );
287     bool get_explicit_flags( void ) const;
288 
289     // -------------------------------------------------------------------- //
290     //  Used only by LUIGI loader                                           //
291     // -------------------------------------------------------------------- //
292     void copy_feature_flag_to_metadata( void );
set_scramble_druid(const bool is_scrambled_,const uint8_t * druid_=nullptr)293     void set_scramble_druid( const bool is_scrambled_,
294                              const uint8_t* druid_ = nullptr )
295     {
296         is_scrambled = is_scrambled_ && druid_;
297         if ( is_scrambled )
298             std::copy( druid_, druid_ + sizeof( druid ), std::begin( druid ) );
299         else
300             std::fill( std::begin( druid ), std::end( druid ), 0 );
301     }
302 };
303 
304 #endif
305