From e2ba8458a5cfdfacfaf103e7ba97d610afa6c970 Mon Sep 17 00:00:00 2001 From: "B. Watson" Date: Mon, 29 Aug 2022 16:11:13 -0400 Subject: initial commit --- cart.c | 261 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 261 insertions(+) create mode 100644 cart.c (limited to 'cart.c') diff --git a/cart.c b/cart.c new file mode 100644 index 0000000..09c1c12 --- /dev/null +++ b/cart.c @@ -0,0 +1,261 @@ +/* CART header support library */ + +#include +#include +#include "cart.h" + +/* 20071227 bkw: + cart_types list was made from atari800-2.0.3/DOC/cart.txt + I didn't use any code from atari800, since it's GPL and this code + is WTFPL. + + Format of CART header (from atari800 cart.txt): + + first 4 bytes containing 'C' 'A' 'R' 'T'. + next 4 bytes containing cartridge type in MSB format (see the table below). + next 4 bytes containing cartridge checksum in MSB format (ROM only). + next 4 bytes are currently unused (zero). + followed immediately with the ROM data: 4, 8, 16, 32, 40, 64, 128, 256, 512 + or 1024 kilobytes. +*/ + +/* 20071227 bkw: this list is complete as of Atari800 2.0.3 */ +/* 20220827 bkw: list now complete as of Atari800 5.0.0 */ +cart_t cart_types[] = { + /* Id */ /* Machine, Size, Name */ + /* 0 */ { M_INVALID, 0, 0 }, /* 0 is invalid type */ + /* 1 */ { M_ATARI8, 8, "Standard 8 KB" }, + /* 2 */ { M_ATARI8, 16, "Standard 16 KB" }, + /* 3 */ { M_ATARI8, 16, "OSS two chip 16 KB cartridge (034M)" }, + /* 4 */ { M_5200, 32, "Standard 32 KB 5200" }, + /* 5 */ { M_ATARI8, 32, "DB 32 KB" }, + /* 6 */ { M_5200, 16, "Two chip 16 KB 5200" }, + /* 7 */ { M_5200, 40, "Bounty Bob Strikes Back 40 KB 5200" }, + /* 8 */ { M_ATARI8, 64, "64 KB Williams" }, + /* 9 */ { M_ATARI8, 64, "Express 64 KB" }, + /* 10 */ { M_ATARI8, 64, "Diamond 64 KB" }, + /* 11 */ { M_ATARI8, 64, "SpartaDos X 64 KB" }, + /* 12 */ { M_ATARI8, 32, "XEGS 32 KB" }, + /* 13 */ { M_ATARI8, 64, "XEGS 64 KB (banks 0-7)" }, + /* 14 */ { M_ATARI8, 128, "XEGS 128 KB" }, + /* 15 */ { M_ATARI8, 16, "OSS one chip 16 KB" }, + /* 16 */ { M_5200, 16, "One chip 16 KB 5200" }, + /* 17 */ { M_ATARI8, 128, "Decoded Atrax 128 KB" }, + /* 18 */ { M_ATARI8, 40, "Bounty Bob Strikes Back 40 KB" }, + /* 19 */ { M_5200, 8, "Standard 8 KB 5200" }, + /* 20 */ { M_5200, 4, "Standard 4 KB 5200" }, + /* 21 */ { M_ATARI8, 8, "Right slot 8 KB" }, + /* 22 */ { M_ATARI8, 32, "32 KB Williams" }, + /* 23 */ { M_ATARI8, 256, "XEGS 256 KB" }, + /* 24 */ { M_ATARI8, 512, "XEGS 512 KB" }, + /* 25 */ { M_ATARI8, 1024, "XEGS 1 MB" }, + /* 26 */ { M_ATARI8, 16, "MegaCart 16 KB" }, + /* 27 */ { M_ATARI8, 32, "MegaCart 32 KB" }, + /* 28 */ { M_ATARI8, 64, "MegaCart 64 KB" }, + /* 29 */ { M_ATARI8, 128, "MegaCart 128 KB" }, + /* 30 */ { M_ATARI8, 256, "MegaCart 256 KB" }, + /* 31 */ { M_ATARI8, 512, "MegaCart 512 KB" }, + /* 32 */ { M_ATARI8, 1024, "MegaCart 1 MB" }, + /* 33 */ { M_ATARI8, 32, "Switchable XEGS 32 KB" }, + /* 34 */ { M_ATARI8, 64, "Switchable XEGS 64 KB" }, + /* 35 */ { M_ATARI8, 128, "Switchable XEGS 128 KB" }, + /* 36 */ { M_ATARI8, 256, "Switchable XEGS 256 KB" }, + /* 37 */ { M_ATARI8, 512, "Switchable XEGS 512 KB" }, + /* 38 */ { M_ATARI8, 1024, "Switchable XEGS 1 MB" }, + /* 39 */ { M_ATARI8, 8, "Phoenix 8 KB" }, + /* 40 */ { M_ATARI8, 16, "Blizzard 16 KB" }, + /* 41 */ { M_ATARI8, 128, "Atarimax 128 KB Flash" }, + /* 42 */ { M_ATARI8, 1024, "Atarimax 1 MB Flash (old)" }, + /* 43 */ { M_ATARI8, 128, "SpartaDos X 128 KB" }, + /* 44 */ { M_ATARI8, 8, "OSS 8 KB" }, + /* 45 */ { M_ATARI8, 16, "OSS two chip 16 KB (043M)" }, + /* 46 */ { M_ATARI8, 4, "Blizzard 4 KB" }, + /* 47 */ { M_ATARI8, 32, "AST 32 KB" }, + /* 48 */ { M_ATARI8, 64, "Atrax SDX 64 KB" }, + /* 49 */ { M_ATARI8, 128, "Atrax SDX 128 KB" }, + /* 50 */ { M_ATARI8, 64, "Turbosoft 64 KB" }, + /* 51 */ { M_ATARI8, 128, "Turbosoft 128 KB" }, + /* 52 */ { M_ATARI8, 32, "Ultracart 32 KB" }, + /* 53 */ { M_ATARI8, 8, "Low bank 8 KB" }, + /* 54 */ { M_ATARI8, 128, "SIC! 128 KB" }, + /* 55 */ { M_ATARI8, 256, "SIC! 256 KB" }, + /* 56 */ { M_ATARI8, 512, "SIC! 512 KB" }, + /* 57 */ { M_ATARI8, 2, "Standard 2 KB" }, + /* 58 */ { M_ATARI8, 4, "Standard 4 KB" }, + /* 59 */ { M_ATARI8, 4, "Right slot 4 KB" }, + /* 60 */ { M_ATARI8, 32, "Blizzard 32 KB" }, + /* 61 */ { M_ATARI8, 2048, "MegaMax 2 MB" }, + /* 62 */ { M_ATARI8,131072, "The!Cart 128 MB" }, + /* 63 */ { M_ATARI8, 4096, "Flash MegaCart 4 MB" }, + /* 64 */ { M_ATARI8, 2048, "MegaCart 2 MB" }, + /* 65 */ { M_ATARI8, 32768, "The!Cart 32 MB" }, + /* 66 */ { M_ATARI8, 65536, "The!Cart 64 MB" }, + /* 67 */ { M_ATARI8, 64, "XEGS 64 KB (banks 8-15)" }, + /* 68 */ { M_ATARI8, 128, "Atrax 128 KB" }, + /* 69 */ { M_ATARI8, 32, "aDawliah 32 KB" }, + /* 70 */ { M_ATARI8, 64, "aDawliah 64 KB" }, + /* 71 */ { M_5200, 64, "Super Cart 64 KB 5200" }, + /* 72 */ { M_5200, 128, "Super Cart 128 KB 5200" }, + /* 73 */ { M_5200, 256, "Super Cart 256 KB 5200" }, + /* 74 */ { M_5200, 512, "Super Cart 512 KB 5200" }, + /* 75 */ { M_ATARI8, 1024, "Atarimax 1 MB Flash (new)" }, +}; + +const int MAX_CART_TYPE = (sizeof(cart_types)/sizeof(cart_t))-1; + +/* helper functions. + get_msb_dword() and put_msb_dword() are written the way they are in + order to avoid endian-ness issues caused by casts or calls to some + endian-aware function like htonl(). +*/ + +static unsigned int get_msb_dword(unsigned char *cart, int offset) { + return + (cart[offset] << 24) | + (cart[offset+1] << 16) | + (cart[offset+2] << 8) | + (cart[offset+3]); +} + +static void put_msb_dword(unsigned char *cart, int offset, unsigned int data) { + int i; + for(i=3; i>=0; i--) { + cart[offset+i] = data & 0xff; + data >>= 8; + } +} + +/* has_cart_signature() returns boolean */ +int has_cart_signature(unsigned char *cart) { + return memcmp(cart, CART_SIGNATURE, 4) == 0; +} + +void cart_dump_header(unsigned char *cart, int calc_cksum) { + char *type = "UNKNOWN"; + unsigned int id = get_cart_type(cart); + unsigned int real_id = get_msb_dword(cart, CART_TYPE_OFFSET); + + if(id) + type = cart_types[id].name; + else + calc_cksum = 0; + + /* Show real type, if get_cart_type() returned zero */ + fprintf(stderr, "CART signature: %s\n", + (has_cart_signature(cart) ? "Present" : "MISSING")); + fprintf(stderr, "Cartridge type: %s (ID %d)\n", type, real_id); + fprintf(stderr, "ROM size: "); + if(id) + fprintf(stderr, "%d bytes + 16 byte header\n", get_cart_size(cart)); + else + fprintf(stderr, "UNKNOWN\n"); + + fprintf(stderr, "Machine type: "); + switch(get_cart_machine(cart)) { + case M_ATARI8: + fprintf(stderr, "Atari 8-bit computer\n"); + break; + + case M_5200: + fprintf(stderr, "Atari 5200\n"); + break; + + default: + fprintf(stderr, "UNKNOWN\n"); + break; + } + + fprintf(stderr, "CART checksum: 0x%08x", get_cart_checksum(cart)); + if(calc_cksum) { + fputs((cart_checksum_ok(cart) ? "OK" : "BAD"), stderr); + } + fputc('\n', stderr); +} + +/* get_cart_type() returns the cartridge ID from the CART header, or zero + if it's an invalid/unknown type. */ +unsigned int get_cart_type(unsigned char *cart) { + unsigned int type = get_msb_dword(cart, CART_TYPE_OFFSET); + + if(type == 0 || type > MAX_CART_TYPE) + return 0; + + return type; +} + +/* get_cart_size() returns the cartridge ROM size in bytes, NOT including + the 16-byte header, or zero if invalid type */ +unsigned int get_cart_size(unsigned char *cart) { + unsigned int type = get_cart_type(cart); + if(type == 0) + return 0; + + return cart_types[type].size * 1024; +} + +/* get_cart_checksum() returns the checksum stored in the header. It does + NOT calculate the checksum (use calc_rom_checksum() for that) */ +unsigned int get_cart_checksum(unsigned char *cart) { + return get_msb_dword(cart, CART_CHECKSUM_OFFSET); +} + +machine_t get_cart_machine(unsigned char *cart) { + unsigned int type = get_cart_type(cart); + if(type == 0) + return M_INVALID; + + return cart_types[type].machine; +} + +/* calc_rom_checksum() is the odd man out: it takes a pointer to the + raw ROM data, NOT to the header+data like all the other functions do! */ +unsigned int calc_rom_checksum(unsigned char *rom, int bytes) { + unsigned int cksum = 0; + + while(bytes-- > 0) cksum += *rom++; + + return cksum; +} + +/* cart_checksum_ok() must be called with a complete .CAR image, not just + a header! */ +unsigned int cart_checksum_ok(unsigned char *cart) { + unsigned int bytes = get_cart_size(cart); + unsigned int got = calc_rom_checksum(cart + 16, bytes); + unsigned int expected = get_cart_checksum(cart); + + return (got == expected); +} + +/* create_cart_header() must be supplied with a buffer, which must be at least + 16 bytes in size. Returns 0 with buffer unchanged if type was invalid. */ +int create_cart_header( + unsigned char *buffer, + unsigned char *rom, + unsigned int type) +{ + if(type == 0 || type > MAX_CART_TYPE) + return 0; + + memcpy(buffer, CART_SIGNATURE, 4); + + set_cart_type(buffer, type); + set_cart_checksum(buffer, + calc_rom_checksum(rom, cart_types[type].size * 1024)); + set_cart_unused(buffer, 0); + + return 1; +} + +void set_cart_checksum(unsigned char *cart, unsigned int sum) { + put_msb_dword(cart, CART_CHECKSUM_OFFSET, sum); +} + +void set_cart_type(unsigned char *cart, unsigned int type) { + put_msb_dword(cart, CART_TYPE_OFFSET, type); +} + +void set_cart_unused(unsigned char *cart, unsigned int data) { + put_msb_dword(cart, CART_UNUSED_OFFSET, data); +} + -- cgit v1.2.3