/* ====== ROMS.C ====== This file manages loading of binary data in ROMs or cartridges. For cartridge lists, etc., see moduledb.c. */ #include "OSLib.h" #include "v9t9_common.h" #include "memory.h" #include "roms.h" #include "fiad.h" #define _L LOG_ROMS | LOG_INFO /* Find the location for a binary image and return size. */ int findbinary(char *path, char *syspath, char *filename, OSSpec *spec) { OSRef ref; OSSize size; OSError err; if (!filename) return 0; if (OS_GetFileNamePtr(filename) != filename && (err = OS_MakeFileSpec(filename, spec)) == OS_NOERR && OS_Status(spec) == OS_NOERR) { // found it } else if ((err = OS_FindFileInPath(filename, path, spec)) != OS_NOERR && (err = OS_FindFileInPath(filename, syspath, spec)) != OS_NOERR) { return 0; } if ((err = OS_Open(spec, OSReadOnly, &ref)) != OS_NOERR) { OSerror(err, "\n"); return 0; } OS_GetSize(ref, &size); OS_Close(ref); return size; } /* Load a binary image, return # bytes read. */ int loadbinary(char *path, char *syspath, char *filename, char *type, u8 * loadat, int swap, int fileoffs, int imagesize, int maxsize) { OSRef ref; OSSize size; OSError err; OSSpec spec; size = findbinary(path, syspath, filename, &spec); if (!size) { if (*filename) { logger(_L |LOG_USER|LOG_ERROR, "Cannot find '%s' in paths:\n%s or\n%s", filename, path, syspath); } return 0; } logger(_L|L_0, "Loading %s image %s... ", type, OS_SpecToString1(&spec)); if (imagesize) size = imagesize; else if (size - fileoffs > maxsize) { logger(_L | LOG_WARN | LOG_USER, "%s too long, only %d bytes of %d read... ", OS_SpecToString1(&spec), maxsize, size); size = maxsize - fileoffs; } if ((err = OS_Open(&spec, OSReadOnly, &ref)) != OS_NOERR) { OSerror(err, "\n"); return 0; } // size -= offset; // loadat += offset; if ((err = OS_Seek(ref, OSSeekAbs, fileoffs)) != OS_NOERR || (err = OS_Read(ref, loadat, &size)) != OS_NOERR) { OSerror(err, "could not read\n"); return 0; } if (swap) { logger(_L, "swapping bytes... "); swab((const void *) loadat, (void *) loadat, size); } logger(_L| L_0, "done\n"); OS_Close(ref); return size; } /* Save a binary image, return # bytes written. */ #if BEWORKS_FS OSFileType osBinaryType = { 0666, "x/v9t9-memory-image" }; #elif POSIX_FS OSFileType osBinaryType = { 0666 }; #elif WIN32_FS OSFileType osBinaryType = 0; #elif MAC_FS OSFileType osBinaryType = 'BIN '; #endif /* * Save data to an image found somewhere in 'path' or 'syspath' * (if not existing, placed in first entry of 'path'). * Existing file is not overwritten. * * path / syspath: search paths * type: string emitted in error, i.e., "RAM image" * filename: name of image * fileoffs: offset into file to write * saveat: start of memory to write * swap: if true, byteswap word data to TI format * memsize: size of memory to write */ int savebinary(char *path, char *syspath, char *type, char *filename, int fileoffs, u8 * saveat, int swap, int memsize) { OSRef ref; OSSize size; OSError err; OSSpec spec; if ((size = findbinary(path, syspath, filename, &spec)) == 0) { if ((err = OS_CreateFileInPath(filename, *path ? path : syspath, &spec, &osBinaryType)) != OS_NOERR) { OSerror(err, "Couldn't create %s image %s anywhere in path: %s\n", type, filename, path); return 0; } } logger(_L |L_0, "Writing %s image %s... ", type, OS_SpecToString1(&spec)); if ((err = OS_Open(&spec, OSReadWrite, &ref)) != OS_NOERR) { OSerror(err, "\n"); return 0; } //size = memsize - offset; //saveat += offset; if (swap) { logger(_L, "swapping bytes to save... "); swab((const void *) saveat, (void *) saveat, size); } if ((err = OS_Seek(ref, OSSeekAbs, fileoffs)) != OS_NOERR || (err = OS_Write(ref, saveat, &size)) != OS_NOERR) { OSerror(err, "could not write\n"); return 0; } if (swap) { logger(_L, "swapping bytes back... "); swab((const void *) saveat, (void *) saveat, size); } logger(_L|L_0, "done\n"); OS_Close(ref); return size; } /* Return 1 if loaded. */ int loaddsr(char *path, char *syspath, char *filename, char *name, void *mem) { return loadbinary(path, syspath, filename, name, mem, 1, 0, 8192, 8192); } /* Return 1 if loaded. */ /* int loadspeech(char *path, char *syspath, char *filename, void *mem) { return loadbinary(path, syspath, filename, "Speech ROM", mem, 0, 0, 32768, 32768); } */ /* Each GRAM Kracker stores an 8K ROM or GROM bank. The first six bytes are an extension of the memory image program standard. Here is what the bytes mean: Byte 0: "More to load" flag, values: >FF = There's another file to load >80 = There's a "UTIL" file to load (I don't think this is ever used). >00 = This is the last file Byte 1: Bank of GRAM/RAM to load file into, values: >01 = GROM/GRAM bank 0 (>0000) >02 = GROM/GRAM bank 1 (>2000) >03 = GROM/GRAM bank 2 (>4000) >04 = GROM/GRAM bank 3 (>6000) >05 = GROM/GRAM bank 4 (>8000) >06 = GROM/GRAM bank 5 (>A000) >07 = GROM/GRAM bank 6 (>C000) >08 = GROM/GRAM bank 7 (>E000) >09 = ROM bank 1 (>6000) >0A = ROM bank 2 (second >6000) >00 or >FF = Assembly language program, not a cartridge file Bytes 2 and 3: Number of bytes to load (normally 8192) Bytes 4 and 5: Actual address to start loading at (e.g. >6000) As an example, Extended BASIC gets saved as files "XB" to "XB5", which have headers as follows: XB FF0A 2000 6000 (ROM bank 2) XB1 FF09 2000 6000 (ROM bank 1) XB2 FF07 2000 C000 (GRAM bank 6) XB3 FF06 2000 A000 (GRAM bank 5) XB4 FF05 2000 8000 (GRAM bank 4) XB5 0004 2000 6000 Even though TI GROMS only use 6K of each 8K bank, all 8K is stored so people can add their own GPL code as extensions to modules. Extended BASIC has a number of enhancements available. */ /* * Search paths for a file matching 'filename'. * If found, and it is a valid GRAM-Kracker file, * set 'spec' to point to it, and return its header info in 'header' * */ int roms_find_gram_kracker(char *path, char *systempath, char *filename, OSSpec *spec, gram_kracker_header *header) { int size; fiad_logger_func old; int old_unknownfileistext; fiad_tifile tf; OSError err; OSRef ref; bool is_v9t9_file; memset(header, 0, sizeof(gram_kracker_header)); /* Can we find the file? */ size = findbinary(path, systempath, filename, spec); if (!size) return 0; /* See if it's a V9t9/TIFILES file or perhaps a plain binary */ old = fiad_set_logger(0L); old_unknownfileistext = unknownfileistext; unknownfileistext = 0; if (fiad_tifile_setup_spec_with_spec(&tf, spec) != OS_NOERR) { goto error_exit; } /* Is it a V9t9 file? */ if (fiad_tifile_get_info(&tf)) { if (!(tf.fdr.flags & ff_program)) { logger(_L|LOG_ERROR|LOG_USER, "GRAM Kracker segment '%s' is not a PROGRAM file (it's %s)\n", OS_SpecToString1(spec), fiad_catalog_get_file_type_string(&tf.fdr)); goto error_exit; } // allow enough space for the data, header, and extra sector if (FDR_FILESIZE(&tf.fdr) > 8198+256) { logger(_L|LOG_ERROR|LOG_USER, "GRAM Kracker segment '%s' is too long\n" "to be a segment (%d bytes > 8192+6 bytes)\n", OS_SpecToString1(spec), FDR_FILESIZE(&tf.fdr)); goto error_exit; } is_v9t9_file = true; } else { is_v9t9_file = false; } /* Read header from first sector */ if (is_v9t9_file) { u16 headersize = 6; if (fiad_tifile_open_file(&tf, newfileformat, false /*create*/, false /*always*/, true /*readonly*/) != OS_NOERR || fiad_tifile_read_binary_image(&tf, (u8 *)header, headersize, &headersize) != 0 || headersize < 6) { logger(_L|LOG_ERROR|LOG_USER, "Could not read signature for GRAM Kracker segment in V9t9 file '%s' (%s)\n", OS_SpecToString1(spec), OS_GetErrText(tf.error)); goto error_exit; } header->absolute_image_file_offset = sizeof(v9t9_fdr) + 6; } else { OSSize headersize = 6; if ((err = OS_Open(spec, OSReadOnly, &ref)) != OS_NOERR || (err = OS_Read(ref, (void *)header, &headersize)) != OS_NOERR || (err = OS_Close(ref)) != OS_NOERR) { logger(_L|LOG_ERROR|LOG_USER, "Could not read signature for GRAM Kracker segment in binary file '%s' (%s)\n", OS_SpecToString1(spec), OS_GetErrText(err)); goto error_exit; } header->absolute_image_file_offset = 6; } /* Make header suitable for v9t9 */ header->address = TI2HOST(header->address); header->length = TI2HOST(header->length); if (header->more_to_load == 0x80) { logger(_L|LOG_ERROR|LOG_USER, "GRAM Kracker segment '%s' is a 'UTIL' segment (not supported)\n", OS_SpecToString1(spec)); goto error_exit; } if (header->gk_type == 0x00 || header->gk_type == 0xff || header->gk_type > GK_TYPE_ROM_2) { logger(_L|LOG_ERROR|LOG_USER, "GRAM Kracker segment '%s' is not a ROM file (got >%02X)\n", OS_SpecToString1(spec), header->gk_type); goto error_exit; } return size; error_exit: fiad_set_logger(old); unknownfileistext = old_unknownfileistext; return 0; }