11/* A work-in-progess MEGA65 (Commodore 65 clone origins) emulator
22 Part of the Xemu project, please visit: https://github.com/lgblgblgb/xemu
3- Copyright (C)2016-2024 LGB (Gábor Lénárt) <[email protected] > 3+ Copyright (C)2016-2025 LGB (Gábor Lénárt) <[email protected] > 44
55This program is free software; you can redistribute it and/or modify
66it under the terms of the GNU General Public License as published by
@@ -20,31 +20,45 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
2020#include "cart.h"
2121#include "xemu/emutools_files.h"
2222#include "audio65.h"
23+ #include "string.h"
24+ #include "errno.h"
2325
24- static Uint8 cart_mem [0x10000 ];
25- static int loaded = 0 ;
26+
27+ static struct {
28+ bool loaded ;
29+ bool autostart ;
30+ char * fn ;
31+ char name [0x21 ];
32+ Uint8 mem [0x10000 ];
33+ int total_size ;
34+ int sections ;
35+ } cart ;
2636
2737
2838void cart_init ( void )
2939{
30- memset (cart_mem , 0xFF , sizeof cart_mem );
40+ memset (cart .mem , 0xFF , sizeof cart .mem );
41+ cart .loaded = false;
42+ cart .autostart = false;
43+ cart .name [0 ] = 0 ;
44+ cart .fn = NULL ;
3145}
3246
3347
3448Uint8 cart_read_byte ( unsigned int addr )
3549{
3650 Uint8 data = 0xFF ;
37- if (addr < sizeof (cart_mem ))
38- data = cart_mem [addr ];
39- if (loaded && addr != 0x3FFDF60 ) // see the comment about OPL at cart_write_byte()
40- DEBUGPRINT ("CART: reading byte ($%02X) at $%X" NL , data , addr + 0x4000000 );
51+ if (addr < sizeof (cart . mem ))
52+ data = cart . mem [addr ];
53+ // if (cart. loaded && addr != 0x3FFDF60) // see the comment about OPL at cart_write_byte()
54+ // DEBUGPRINT("CART: reading byte ($%02X) at $%X" NL, data, addr + 0x4000000);
4155 return data ;
4256}
4357
4458
4559void cart_write_byte ( unsigned int addr , Uint8 data )
4660{
47- if (!loaded ) {
61+ if (!cart . loaded ) {
4862 // a hack OPL to be able to work in the "slow device area" [if no cartridge is loaded]
4963 static Uint8 opl_reg_sel = 0 ;
5064 if (addr == 0x3FFDF40 ) {
@@ -55,43 +69,138 @@ void cart_write_byte ( unsigned int addr, Uint8 data )
5569 return ;
5670 }
5771 }
58- DEBUGPRINT ("CART: writing byte ($%02X) at $%X" NL , data , addr + 0x4000000 );
72+ // DEBUGPRINT("CART: writing byte ($%02X) at $%X" NL, data, addr + 0x4000000);
5973}
6074
6175
62- int cart_load_bin ( const char * fn , const unsigned int addr , const char * cry )
76+ bool cart_is_attached ( void )
6377{
64- if (!fn || !* fn )
65- return 0 ;
66- loaded = 0 ;
67- if (addr >= sizeof (cart_mem )) {
68- if (cry )
69- ERROR_WINDOW ("%s\nOutside of 64K range\n%s" , cry , fn );
70- return -1 ;
78+ return cart .loaded ;
79+ }
80+
81+
82+ void cart_detach ( void )
83+ {
84+ memset (cart .mem , 0xFF , sizeof cart .mem );
85+ cart .autostart = false;
86+ free (cart .fn );
87+ cart .fn = NULL ;
88+ if (cart .loaded ) {
89+ DEBUGPRINT ("CART: cartridge has been detached" NL );
90+ cart .loaded = false;
7191 }
72- const int ret = xemu_load_file (fn , cart_mem + addr , 1 , sizeof (cart_mem ) - addr , cry );
73- if (ret <= 0 )
74- return -1 ;
75- DEBUGPRINT ("CART: %d byte(s) loaded at offset $%04X from file %s" NL , ret , addr , fn );
76- loaded = 1 ;
77- return 0 ;
7892}
7993
8094
81- int cart_detect_id ( void )
95+ static bool _check_cartridge_str ( const Uint8 * p )
8296{
83- static const Uint8 cart_id [] = {'M' , '6' , '5' };
84- return memcmp (cart_mem + 0x8007 , cart_id , sizeof cart_id );
97+ static const char cartridge_bytestr [] = { ' ' , 'C' , 'A' , 'R' , 'T' , 'R' , 'I' , 'D' , 'G' , 'E' };
98+ for (unsigned int a = 3 ; a < 7 ; a ++ )
99+ if (!memcmp (p + a , cartridge_bytestr , sizeof cartridge_bytestr ))
100+ return true;
101+ return false;
85102}
86103
87104
88- int cart_is_loaded ( void )
105+ int cart_attach ( const char * fn )
89106{
90- return loaded ;
107+ static const char m65_bytestr [] = { 'M' , '6' , '5' };
108+ static const char mega65_bytestr [] = { 'M' , 'E' , 'G' , 'A' , '6' , '5' };
109+ static const char chip_bytestr [] = { 'C' , 'H' , 'I' , 'P' };
110+ cart_detach ();
111+ if (!fn || !* fn )
112+ return -1 ;
113+ const int fd = xemu_open_file (fn , O_RDONLY , NULL , NULL );
114+ if (fd < 0 ) {
115+ ERROR_WINDOW ("Cannot open cartridge file %s\nError: %s" , fn , strerror (errno ));
116+ return -1 ;
117+ }
118+ cart .fn = xemu_realloc (cart .fn , strlen (fn ) + 1 );
119+ strcpy (cart .fn , fn );
120+ Uint8 buf [0x40 + 1 ];
121+ if (xemu_safe_read (fd , buf , 0x40 ) != 0x40 )
122+ goto read_error ;
123+ if (!_check_cartridge_str (buf )) {
124+ // " CARTRIDGE" (with space) cannot be found in the first 0x10 bytes: not a VICE CRT format
125+ // let's check if it's a "raw ROM" with the "M65" mark
126+ if (memcmp (buf + 7 , m65_bytestr , sizeof m65_bytestr )) {
127+ ERROR_WINDOW ("Not a CRT/raw file: %s" , fn ); // not that either -> error
128+ goto error ;
129+ }
130+ // Raw binary file, with "M65" mark: the best I can do is loading from $8000
131+ memcpy (cart .mem + 0x8000 , buf , 0x40 ); // copy what we have already
132+ const int ret = xemu_safe_read (fd , cart .mem + 0x8000 + 0x40 , 0x10000 - 0x8000 - 0x40 ); // load the rest
133+ if (ret < 0 )
134+ goto read_error ;
135+ cart .loaded = true;
136+ cart .sections = 0 ;
137+ cart .total_size = ret + 0x40 ;
138+ cart .autostart = true; // raw image is always auto start as I detected with 'M65' signature ...
139+ DEBUGPRINT ("CART: raw cartridge ROM image \"%s\" has been loaded to $8000-$%X" NL , fn , 0x8000 + cart .total_size - 1 );
140+ close (fd );
141+ return 1 ;
142+ }
143+ if (memcmp (buf , mega65_bytestr , sizeof mega65_bytestr )) {
144+ ERROR_WINDOW ("Non-MEGA65 CRT file: %s" , fn );
145+ goto error ;
146+ }
147+ // VICE-like CRT file (MEGA65-specific though)
148+ DEBUGPRINT ("CART: trying to attach VICE-like CRT file %s" NL , fn );
149+ buf [0x40 ] = 0 ;
150+ strcpy (cart .name , (const char * )buf + 0x20 );
151+ cart .sections = 0 ;
152+ cart .total_size = 0 ;
153+ for (;;) {
154+ const int ret = xemu_safe_read (fd , buf , 0x10 ); // read "CHIP" section header
155+ if (!ret )
156+ break ;
157+ if (ret != 0x10 )
158+ goto read_error ;
159+ if (memcmp (buf , chip_bytestr , sizeof chip_bytestr )) {
160+ ERROR_WINDOW ("Bad CRT file, missing CHIP section ID" );
161+ goto error ;
162+ }
163+ const unsigned int size = (buf [0xE ] << 8 ) + buf [0xF ];
164+ const unsigned int addr = (buf [0xC ] << 8 ) + buf [0xD ];
165+ DEBUGPRINT ("CART: ... new CHIP section (#%d), $%04X-$%04X (%u bytes)" NL , cart .sections , addr , addr + size - 1 , size );
166+ if (size + addr > 0x10000 ) {
167+ ERROR_WINDOW ("Bad CRT file, CHIP section overflows memory" );
168+ goto error ;
169+ }
170+ if (size ) {
171+ if (xemu_safe_read (fd , cart .mem + addr , size ) != size )
172+ goto read_error ;
173+ }
174+ cart .sections ++ ;
175+ cart .total_size += size ;
176+ }
177+ if (!cart .sections ) {
178+ ERROR_WINDOW ("No CHIP sections" );
179+ goto error ;
180+ }
181+ cart .loaded = true;
182+ cart .autostart = !memcmp (cart .mem + 0x8007 , m65_bytestr , sizeof m65_bytestr );
183+ DEBUGPRINT ("CART: attached, autostart = %d, name = \"%s\"" NL , (int )cart .autostart , cart .name );
184+ close (fd );
185+ return (int )cart .autostart ;
186+ read_error :
187+ ERROR_WINDOW ("Cannot read (or truncated/too small) cartridge file %s" , fn );
188+ error :
189+ cart_detach ();
190+ if (fd >= 0 )
191+ close (fd );
192+ return -1 ;
91193}
92194
93195
94- void cart_copy_from ( const Uint16 cart_addr , Uint8 * target , const Uint16 size )
196+ void cart_info ( char * p , size_t size )
95197{
96- memcpy (target , cart_mem + cart_addr , size );
198+ if (!cart .loaded ) {
199+ snprintf (p , size , "No cartridge is attached" );
200+ return ;
201+ }
202+ snprintf (p , size , "Filename: %s\nCartridge: %s\nAuto-start: %d\nSections: %d\nTotal binary size: %d\nFormat: %s" ,
203+ cart .fn , cart .name , (int )cart .autostart , cart .sections , cart .total_size ,
204+ cart .sections ? "CRT" : "RAW"
205+ );
97206}
0 commit comments