@@ -5,29 +5,31 @@ use crate::utils::{rgb5_to_rgb8, HeapMemU8, NoHashMap};
55use static_assertions:: const_assert_eq;
66use std:: cmp:: min;
77use std:: fs:: File ;
8+ use std:: intrinsics:: unlikely;
89use std:: io:: { ErrorKind , Seek } ;
910use std:: ops:: { Deref , DerefMut } ;
1011use std:: os:: unix:: fs:: FileExt ;
1112use std:: path:: PathBuf ;
12- use std:: sync:: { Arc , Mutex } ;
13- use std:: time:: Instant ;
14- use std:: { io, mem} ;
13+ use std:: sync:: atomic:: AtomicBool ;
14+ use std:: sync:: { Arc , Condvar , Mutex } ;
15+ use std:: time:: { Duration , Instant } ;
16+ use std:: { io, mem, thread} ;
1517
16- #[ repr( C , packed ) ]
18+ #[ repr( C ) ]
1719pub struct ArmValues {
1820 pub rom_offset : u32 ,
1921 pub entry_address : u32 ,
2022 pub ram_address : u32 ,
2123 pub size : u32 ,
2224}
2325
24- #[ repr( C , packed ) ]
26+ #[ repr( C ) ]
2527pub struct ArmOverlay {
26- overlay_offset : u32 ,
27- overlay_size : u32 ,
28+ pub overlay_offset : u32 ,
29+ pub overlay_size : u32 ,
2830}
2931
30- #[ repr( C , packed ) ]
32+ #[ repr( C ) ]
3133pub struct CartridgeHeader {
3234 game_title : [ u8 ; 12 ] ,
3335 pub game_code : [ u8 ; 4 ] ,
@@ -42,10 +44,8 @@ pub struct CartridgeHeader {
4244 autostart : u8 ,
4345 pub arm9_values : ArmValues ,
4446 pub arm7_values : ArmValues ,
45- file_name_table_offset : u32 ,
46- file_name_table_size : u32 ,
47- file_allocation_table_offset : u32 ,
48- file_allocation_table_size : u32 ,
47+ pub file_name_table : ArmOverlay ,
48+ pub file_allocation_table : ArmOverlay ,
4949 arm9_overlay : ArmOverlay ,
5050 arm7_overlay : ArmOverlay ,
5151 port_setting_normal_commands : u32 ,
@@ -162,16 +162,46 @@ impl CartridgePreview {
162162 }
163163}
164164
165+ #[ derive( Default ) ]
166+ struct CartridgeContent {
167+ content_pages : NoHashMap < u32 , u16 > ,
168+ content_cache : HeapMemU8 < MAX_CARTRIDGE_CACHE > ,
169+ }
170+
171+ impl CartridgeContent {
172+ fn get_page ( & mut self , file : & File , page_addr : u32 ) -> io:: Result < * const [ u8 ; CARTRIDGE_PAGE_SIZE ] > {
173+ debug_assert_eq ! ( page_addr & ( CARTRIDGE_PAGE_SIZE as u32 - 1 ) , 0 ) ;
174+ match self . content_pages . get ( & page_addr) {
175+ None => {
176+ if unlikely ( self . content_pages . len ( ) >= MAX_CARTRIDGE_CACHE / CARTRIDGE_PAGE_SIZE ) {
177+ debug_println ! ( "clear cartridge pages" ) ;
178+ self . content_pages . clear ( ) ;
179+ }
180+
181+ let content_offset = self . content_pages . len ( ) as u16 ;
182+ let start = content_offset as usize * CARTRIDGE_PAGE_SIZE ;
183+ let buf = & mut self . content_cache [ start..start + CARTRIDGE_PAGE_SIZE ] ;
184+ file. read_at ( buf, page_addr as u64 ) ?;
185+ self . content_pages . insert ( page_addr, content_offset) ;
186+ Ok ( buf. as_ptr ( ) as _ )
187+ }
188+ Some ( page) => Ok ( self . content_cache [ * page as usize * CARTRIDGE_PAGE_SIZE ..] . as_ptr ( ) as _ ) ,
189+ }
190+ }
191+ }
192+
165193pub struct CartridgeIo {
166194 file : File ,
167195 pub file_name : String ,
168196 pub file_size : u32 ,
169197 pub header : CartridgeHeader ,
170- content_pages : NoHashMap < u32 , u16 > ,
171- content_cache : HeapMemU8 < MAX_CARTRIDGE_CACHE > ,
198+ content : CartridgeContent ,
172199 save_file_path : PathBuf ,
173200 pub save_file_size : u32 ,
174201 save_buf : Mutex < ( Vec < u8 > , bool ) > ,
202+ async_cmd : Mutex < ( u32 , u32 ) > ,
203+ async_cmd_condvar : Condvar ,
204+ read_lock : Mutex < ( ) > ,
175205}
176206
177207unsafe impl Send for CartridgeIo { }
@@ -209,42 +239,25 @@ impl CartridgeIo {
209239 file_name : preview. file_name ,
210240 file_size,
211241 header : preview. header ,
212- content_pages : NoHashMap :: default ( ) ,
213- content_cache : HeapMemU8 :: new ( ) ,
242+ content : CartridgeContent :: default ( ) ,
214243 save_file_path,
215244 save_file_size,
216245 save_buf : Mutex :: new ( ( save_buf, false ) ) ,
246+ async_cmd : Mutex :: new ( ( 0 , 0 ) ) ,
247+ async_cmd_condvar : Condvar :: new ( ) ,
248+ read_lock : Mutex :: new ( ( ) ) ,
217249 } )
218250 }
219251
220- fn get_page ( & mut self , page_addr : u32 ) -> io:: Result < * const [ u8 ; CARTRIDGE_PAGE_SIZE ] > {
221- debug_assert_eq ! ( page_addr & ( CARTRIDGE_PAGE_SIZE as u32 - 1 ) , 0 ) ;
222- match self . content_pages . get ( & page_addr) {
223- None => {
224- if self . content_pages . len ( ) >= MAX_CARTRIDGE_CACHE / CARTRIDGE_PAGE_SIZE {
225- debug_println ! ( "clear cartridge pages" ) ;
226- self . content_pages . clear ( ) ;
227- }
228-
229- let content_offset = self . content_pages . len ( ) as u16 ;
230- let start = content_offset as usize * CARTRIDGE_PAGE_SIZE ;
231- let buf = & mut self . content_cache [ start..start + CARTRIDGE_PAGE_SIZE ] ;
232- self . file . read_at ( buf, page_addr as u64 ) ?;
233- self . content_pages . insert ( page_addr, content_offset) ;
234- Ok ( buf. as_ptr ( ) as _ )
235- }
236- Some ( page) => Ok ( self . content_cache [ * page as usize * CARTRIDGE_PAGE_SIZE ..] . as_ptr ( ) as _ ) ,
237- }
238- }
239-
240252 pub fn read_slice ( & mut self , offset : u32 , slice : & mut [ u8 ] ) -> io:: Result < ( ) > {
241253 let mut remaining = slice. len ( ) ;
242254 while remaining > 0 {
243255 let slice_start = slice. len ( ) - remaining;
244256
245257 let page_addr = ( offset + slice_start as u32 ) & !( CARTRIDGE_PAGE_SIZE as u32 - 1 ) ;
246258 let page_offset = offset + slice_start as u32 - page_addr;
247- let page = self . get_page ( page_addr) ?;
259+ let _read_lock = self . read_lock . lock ( ) . unwrap ( ) ;
260+ let page = self . content . get_page ( & self . file , page_addr) ?;
248261 let page = unsafe { page. as_ref_unchecked ( ) } ;
249262 let page_slice = & page[ page_offset as usize ..] ;
250263
@@ -324,6 +337,33 @@ impl CartridgeIo {
324337 * dirty = false ;
325338 }
326339 }
340+
341+ pub fn async_cmd ( & self , addr : u32 , len : u32 ) {
342+ let mut async_cmd = self . async_cmd . lock ( ) . unwrap ( ) ;
343+ * async_cmd = ( addr, len) ;
344+ self . async_cmd_condvar . notify_one ( ) ;
345+ }
346+
347+ pub fn async_read_rom ( & mut self ) {
348+ let async_cmd = self . async_cmd . lock ( ) . unwrap ( ) ;
349+ let ( mut async_cmd, timeout_result) = self
350+ . async_cmd_condvar
351+ . wait_timeout_while ( async_cmd, Duration :: from_millis ( 500 ) , |( addr, offset) | * addr == 0 && * offset == 0 )
352+ . unwrap ( ) ;
353+ if timeout_result. timed_out ( ) {
354+ return ;
355+ }
356+
357+ let ( addr, len) = * async_cmd;
358+ * async_cmd = ( 0 , 0 ) ;
359+
360+ let aligned_addr = addr & !( CARTRIDGE_PAGE_SIZE as u32 - 1 ) ;
361+ let aligned_addr_end = ( addr + len + CARTRIDGE_PAGE_SIZE as u32 - 1 ) & !( CARTRIDGE_PAGE_SIZE as u32 - 1 ) ;
362+ for addr in ( aligned_addr..aligned_addr_end) . step_by ( CARTRIDGE_PAGE_SIZE ) {
363+ let _read_lock = self . read_lock . lock ( ) . unwrap ( ) ;
364+ self . content . get_page ( & self . file , addr) . unwrap ( ) ;
365+ }
366+ }
327367}
328368
329369const KEY1_BUF_SIZE : usize = 0x412 ;
0 commit comments