mirror of
https://github.com/MiSTer-devel/Main_MiSTer.git
synced 2026-04-12 03:04:02 +00:00
1059 lines
31 KiB
C++
1059 lines
31 KiB
C++
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <inttypes.h>
|
|
|
|
#include <algorithm>
|
|
#include <vector>
|
|
|
|
#include "../../file_io.h"
|
|
#include "../../user_io.h"
|
|
#include "../../hardware.h"
|
|
#include "../../menu.h"
|
|
#include "../../input.h"
|
|
|
|
#include "c64.h"
|
|
|
|
//#define dbgprintf printf
|
|
#define dbgprintf(...)
|
|
|
|
#define CBM_PRG 0x82
|
|
|
|
#define T64_BYTE_PER_HEADER 32U
|
|
#define T64_FILL_VALUE 0x20
|
|
|
|
#define D64_FILE_STRIDE 10U
|
|
#define D64_DIR_STRIDE 3U
|
|
|
|
#define D64_TRACK_PER_DISK 35U
|
|
#define D64_SECTOR_PER_DISK 683U
|
|
#define D64_FILE_SECTOR_PER_DISK (D64_SECTOR_PER_DISK - 19U)
|
|
#define D64_BYTE_PER_DIR 32U
|
|
#define D64_BYTE_PER_SECTOR 256U
|
|
#define D64_BYTE_PER_FILE_SECTOR 254U
|
|
#define D64_DIR_PER_SECTOR 8U
|
|
#define D64_FILE_PER_DISK 144U
|
|
#define D64_BYTE_PER_BAM_ENTRY 4U
|
|
#define D64_BYTE_PER_STRING 16U
|
|
|
|
#define D64_BAM_TRACK 18U
|
|
#define D64_BAM_SECTOR 0U
|
|
#define D64_DIR_TRACK 18U
|
|
#define D64_DIR_SECTOR 1U
|
|
#define D64_FILE_TRACK 1U // 1-35
|
|
#define D64_FILE_SECTOR 0U
|
|
|
|
#define D64_FILL_VALUE 0xA0
|
|
#define D64_INIT_VALUE 0x00 // DIR relies on 0x00 initial value
|
|
|
|
#define G64_MAX_TRACK_LEN (G64_BLOCK_COUNT_1571 * 256)
|
|
#define G64_TRACK_SPACE_GCR 7930
|
|
#define G64_TRACK_SPACE_MFM 12500
|
|
|
|
// Define EXTEND_1541 to enable creating new tracks in G64 images on the 1541 in the C64/C16/VIC20 cores
|
|
// #define EXTEND_1541
|
|
|
|
struct FileRecord {
|
|
char name[D64_BYTE_PER_STRING];
|
|
unsigned char cbm;
|
|
unsigned short start;
|
|
unsigned short size;
|
|
unsigned offset;
|
|
unsigned index;
|
|
};
|
|
|
|
static bool cmp_offset(const FileRecord& a, const FileRecord& b) { return a.offset < b.offset; }
|
|
static bool cmp_index(const FileRecord& a, const FileRecord& b) { return a.index < b.index; }
|
|
|
|
static unsigned char d64_sector_per_track(unsigned char trackNum) {
|
|
return (trackNum <= 17) ? 21U
|
|
: (trackNum <= 24) ? 19U
|
|
: (trackNum <= 30) ? 18U
|
|
: 17U;
|
|
}
|
|
|
|
static unsigned d64_file_sector(unsigned size) {
|
|
return (size + D64_BYTE_PER_FILE_SECTOR - 1) / D64_BYTE_PER_FILE_SECTOR;
|
|
}
|
|
|
|
static unsigned d64_offset(unsigned char trackNum, unsigned char sectorNum)
|
|
{
|
|
static unsigned TrackFileOffset[D64_TRACK_PER_DISK] = {
|
|
0x00000, /* 1 */ 0x01500, /* 2 */ 0x02A00, /* 3 */ 0x03F00, /* 4 */ 0x05400, /* 5 */
|
|
0x06900, /* 6 */ 0x07E00, /* 7 */ 0x09300, /* 8 */ 0x0A800, /* 9 */ 0x0BD00, /* 10 */
|
|
0x0D200, /* 11 */ 0x0E700, /* 12 */ 0x0FC00, /* 13 */ 0x11100, /* 14 */ 0x12600, /* 15 */
|
|
0x13B00, /* 16 */ 0x15000, /* 17 */ 0x16500, /* 18 */ 0x17800, /* 19 */ 0x18B00, /* 20 */
|
|
0x19E00, /* 21 */ 0x1B100, /* 22 */ 0x1C400, /* 23 */ 0x1D700, /* 24 */ 0x1EA00, /* 25 */
|
|
0x1FC00, /* 26 */ 0x20E00, /* 27 */ 0x22000, /* 28 */ 0x23200, /* 29 */ 0x24400, /* 30 */
|
|
0x25600, /* 31 */ 0x26700, /* 32 */ 0x27800, /* 33 */ 0x28900, /* 34 */ 0x29A00, /* 35 */
|
|
};
|
|
|
|
return TrackFileOffset[trackNum - 1] + sectorNum * D64_BYTE_PER_SECTOR;
|
|
}
|
|
|
|
static void d64_advance_pointer(unsigned char& trackNum, unsigned char& sectorNum) {
|
|
unsigned stride = trackNum == D64_DIR_TRACK ? D64_DIR_STRIDE : D64_FILE_STRIDE;
|
|
unsigned sectorPerTrack = d64_sector_per_track(trackNum);
|
|
|
|
sectorNum = (sectorNum + stride) % sectorPerTrack;
|
|
// 18 sectors with a stride of 10 has a common divisor of 2. NOTE: this doesn't handle all combinations of sectorsPerTrack and stride
|
|
if (!(sectorPerTrack & 1) && !(stride & 1) && !(sectorNum >> 1)) sectorNum ^= 1;
|
|
// caller needs to handle DIR track
|
|
if (!sectorNum) trackNum = trackNum % D64_TRACK_PER_DISK + 1;
|
|
}
|
|
|
|
static void d64_advance_dir_pointer(unsigned char& trackNum, unsigned char& sectorNum) {
|
|
unsigned stride = trackNum == D64_DIR_TRACK ? D64_DIR_STRIDE : D64_FILE_STRIDE;
|
|
unsigned sectorPerTrack = d64_sector_per_track(trackNum);
|
|
|
|
// don't leave current track
|
|
sectorNum = (sectorNum + stride) % sectorPerTrack;
|
|
// caller needs to handle BAM sector
|
|
}
|
|
|
|
static int c64_convert_t64_to_d64(fileTYPE* f_in, fileTYPE* f_out)
|
|
{
|
|
std::vector<FileRecord> files;
|
|
char header[T64_BYTE_PER_HEADER];
|
|
char name[D64_BYTE_PER_STRING];
|
|
|
|
if (!FileSeek(f_in, 0, SEEK_SET)) return 0;
|
|
// ignore signature
|
|
if(!FileReadAdv(f_in, header, sizeof(header))) return 0;
|
|
if (memcmp(header, "C64", strlen("C64"))) return 0;
|
|
// header
|
|
if (!FileReadAdv(f_in, header, sizeof(header))) return 0;
|
|
|
|
unsigned short numRecords = (header[0x03] << 8) | header[0x02];
|
|
memcpy(name, header + 0x08, sizeof(name));
|
|
|
|
for (unsigned i = 0; i < numRecords; i++)
|
|
{
|
|
// record
|
|
if (!FileReadAdv(f_in, header, sizeof(header))) return 0;
|
|
if (!header[0x00] || !header[0x01]) continue;
|
|
|
|
FileRecord r;
|
|
r.cbm = CBM_PRG; // header[0x01];
|
|
r.start = (header[0x03] << 8) | header[0x02];
|
|
r.size = ((header[0x05] << 8) | header[0x04]) - r.start;
|
|
r.offset = (header[0x0B] << 24) | (header[0x0A] << 16) | (header[0x09] << 8) | header[0x08];
|
|
memcpy(r.name, header + 0x10, sizeof(r.name));
|
|
r.index = i;
|
|
|
|
files.push_back(r);
|
|
}
|
|
|
|
// workaround incorrect end/limit address
|
|
std::sort(files.begin(), files.end(), cmp_offset);
|
|
for (unsigned i = 0; i < files.size(); i++)
|
|
{
|
|
unsigned short size = (i < files.size() - 1 ? files[i + 1].offset : f_in->size) - files[i].offset;
|
|
if (size < files[i].size) files[i].size = size;
|
|
}
|
|
std::sort(files.begin(), files.end(), cmp_index);
|
|
|
|
// account for start address in PRG format
|
|
for (unsigned i = 0; i < files.size(); i++) files[i].size += 2;
|
|
|
|
// drop files until they fit
|
|
if (files.size() > D64_FILE_PER_DISK) files.resize(D64_FILE_PER_DISK);
|
|
for (unsigned i = 0, fileSectors = 0; i < files.size(); i++)
|
|
{
|
|
fileSectors += d64_file_sector(files[i].size);
|
|
if (fileSectors > D64_FILE_SECTOR_PER_DISK)
|
|
{
|
|
files.resize(i);
|
|
break;
|
|
}
|
|
}
|
|
|
|
//printf("T64: %d records\n", files.size()); for (auto r : files) printf("start: %x, size: %x, offset %x, index: %d\n", r.start, r.size, r.offset, r.index);
|
|
|
|
if (!files.size()) return 0;
|
|
|
|
unsigned char sector[D64_BYTE_PER_SECTOR];
|
|
memset(sector, D64_INIT_VALUE, sizeof(sector));
|
|
for (unsigned i = 0; i < D64_SECTOR_PER_DISK; i++) if (!FileWriteAdv(f_out, sector, sizeof(sector))) return 0;
|
|
if (!FileSeek(f_out, 0, SEEK_SET)) return 0;
|
|
|
|
unsigned char bam[D64_BYTE_PER_SECTOR];
|
|
memset(bam, D64_INIT_VALUE, sizeof(bam));
|
|
|
|
unsigned char dir[D64_BYTE_PER_DIR];
|
|
|
|
unsigned char fileTrackNum = D64_FILE_TRACK, fileSectorNum = D64_FILE_SECTOR, dirTrackNum = D64_DIR_TRACK, dirSectorNum = D64_DIR_SECTOR, dirEntry = 0;
|
|
for (unsigned i = 0; i < files.size(); i++) {
|
|
FileRecord& r = files[i];
|
|
|
|
// DIR sector
|
|
if (dirEntry == 0 && i != 0)
|
|
{
|
|
// set next track/sector pointer of prev node
|
|
if (!FileSeek(f_out, d64_offset(dirTrackNum, dirSectorNum), SEEK_SET)) return 0;
|
|
do d64_advance_dir_pointer(dirTrackNum, dirSectorNum); while (dirTrackNum == D64_BAM_TRACK && dirSectorNum == D64_BAM_SECTOR);
|
|
dir[0x00] = dirTrackNum;
|
|
dir[0x01] = dirSectorNum;
|
|
if (!FileWriteAdv(f_out, dir, 2)) return 0;
|
|
|
|
// check for overflow
|
|
bool success = dirSectorNum != D64_DIR_SECTOR;
|
|
if (!success) printf("T64: dir overflow on file: %d\n", i);
|
|
if (!success) return 0;
|
|
}
|
|
|
|
if (!FileSeek(f_out, d64_offset(dirTrackNum, dirSectorNum) + dirEntry * D64_BYTE_PER_DIR, SEEK_SET)) return 0;
|
|
dir[0x00] = 0x00;
|
|
dir[0x01] = dirEntry == 0 ? 0xFF : 0x00;
|
|
dir[0x02] = r.cbm;
|
|
dir[0x03] = fileTrackNum;
|
|
dir[0x04] = fileSectorNum;
|
|
memcpy(dir + 0x05, r.name, sizeof(r.name));
|
|
for (int o = sizeof(r.name) - 1; o >= 0; o--) if (dir[0x05 + o] != T64_FILL_VALUE) break; else dir[0x05 + o] = D64_FILL_VALUE;
|
|
memset(dir + 0x15, 0x00, 0x17 - 0x15 + 1); // REL
|
|
memset(dir + 0x18, 0x00, 0x1D - 0x18 + 1);
|
|
unsigned sectorSize= d64_file_sector(files[i].size);
|
|
dir[0x1E] = (sectorSize >> 0) & 0xFF;
|
|
dir[0x1F] = (sectorSize >> 8) & 0xFF;
|
|
if (!FileWriteAdv(f_out, dir, sizeof(dir))) return 0;
|
|
|
|
dirEntry = (dirEntry + 1) % D64_DIR_PER_SECTOR;
|
|
|
|
// file sectors
|
|
if (!FileSeek(f_in, r.offset, SEEK_SET)) return 0;
|
|
bool writeAddress = true;
|
|
for (unsigned s = 0; s < r.size; s+= D64_BYTE_PER_FILE_SECTOR)
|
|
{
|
|
if (!FileSeek(f_out, d64_offset(fileTrackNum, fileSectorNum), SEEK_SET)) return 0;
|
|
d64_advance_pointer(fileTrackNum, fileSectorNum);
|
|
if (fileTrackNum == D64_DIR_TRACK) fileTrackNum += 1;
|
|
memset(sector, D64_FILL_VALUE, sizeof(sector));
|
|
|
|
unsigned cnt = std::min(D64_BYTE_PER_FILE_SECTOR, r.size - s);
|
|
sector[0x00] = (s + D64_BYTE_PER_FILE_SECTOR < r.size) ? fileTrackNum : 0x00;
|
|
sector[0x01] = (s + D64_BYTE_PER_FILE_SECTOR < r.size) ? fileSectorNum : (cnt + 2 - 1);
|
|
if (writeAddress)
|
|
{
|
|
sector[0x02] = (r.start >> 0) & 0xFF;
|
|
sector[0x03] = (r.start >> 8) & 0xFF;
|
|
}
|
|
|
|
if (!FileReadAdv(f_in, sector + (writeAddress ? 4 : 2), cnt - (writeAddress ? 2 : 0))) return 0;
|
|
if (!FileWriteAdv(f_out, sector, sizeof(sector))) return 0;
|
|
writeAddress = false;
|
|
}
|
|
}
|
|
|
|
// BAM
|
|
bam[0x00] = D64_DIR_TRACK;
|
|
bam[0x01] = D64_DIR_SECTOR;
|
|
bam[0x02] = 0x41;
|
|
bam[0x03] = 0x00;
|
|
// set available
|
|
while (true) {
|
|
d64_advance_pointer(fileTrackNum, fileSectorNum);
|
|
if (fileTrackNum == D64_FILE_TRACK && fileSectorNum == D64_FILE_SECTOR) break;
|
|
if (fileTrackNum == D64_DIR_TRACK) continue;
|
|
|
|
bam[0x04 + (fileTrackNum - 1) * D64_BYTE_PER_BAM_ENTRY] += 1;
|
|
bam[0x04 + (fileTrackNum - 1) * D64_BYTE_PER_BAM_ENTRY + 1 + fileSectorNum / 8] |= 1 << (fileSectorNum % 8);
|
|
};
|
|
while (true) {
|
|
d64_advance_dir_pointer(dirTrackNum, dirSectorNum);
|
|
if (dirTrackNum == D64_DIR_TRACK && dirSectorNum == D64_DIR_SECTOR) break;
|
|
if (dirTrackNum == D64_BAM_TRACK && dirSectorNum == D64_BAM_SECTOR) continue;
|
|
|
|
bam[0x04 + (dirTrackNum - 1) * D64_BYTE_PER_BAM_ENTRY] += 1;
|
|
bam[0x04 + (dirTrackNum - 1) * D64_BYTE_PER_BAM_ENTRY + 1 + dirSectorNum / 8] |= 1 << (dirSectorNum % 8);
|
|
}
|
|
memcpy(bam + 0x90, name, sizeof(name));
|
|
for (int o = sizeof(name) - 1; o >= 0; o--) if (bam[0x90 + o] != T64_FILL_VALUE) break; else bam[0x90 + o] = D64_FILL_VALUE;
|
|
bam[0xA0] = D64_FILL_VALUE;
|
|
bam[0xA1] = D64_FILL_VALUE;
|
|
bam[0xA2] = 0x30;
|
|
bam[0xA3] = 0x30;
|
|
bam[0xA4] = D64_FILL_VALUE;
|
|
bam[0xA5] = 0x32;
|
|
bam[0xA6] = 0x41;
|
|
memset(bam + 0xA7, D64_FILL_VALUE, 0xAA - 0xA7 + 1);
|
|
memset(bam + 0xAB, 0x00, 0xFF - 0xAB + 1);
|
|
|
|
if (!FileSeek(f_out, d64_offset(D64_BAM_TRACK, D64_BAM_SECTOR), SEEK_SET)) return 0;
|
|
if (!FileWriteAdv(f_out, bam, sizeof(bam))) return 0;
|
|
|
|
f_out->size = FileGetSize(f_out);
|
|
printf("Virtual D64 size = %llu\n", f_out->size);
|
|
return 1;
|
|
}
|
|
|
|
int c64_openT64(const char *path, fileTYPE* f)
|
|
{
|
|
if (!FileOpenEx(f, "vdsk", -1))
|
|
{
|
|
printf("ERROR: fail to create vdsk\n");
|
|
return 0;
|
|
}
|
|
|
|
fileTYPE f_in;
|
|
if (!FileOpen(&f_in, path))
|
|
{
|
|
FileClose(f);
|
|
return 0;
|
|
}
|
|
|
|
int ret = c64_convert_t64_to_d64(&f_in, f);
|
|
FileClose(&f_in);
|
|
|
|
if (!ret)
|
|
{
|
|
printf("Failed to convert T64 (%s).\n", path);
|
|
FileClose(f);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
// -----------------------------------------------------------------------------------------
|
|
|
|
struct img_info
|
|
{
|
|
fileTYPE *f;
|
|
int type;
|
|
uint8_t tracks;
|
|
uint8_t id[2];
|
|
uint32_t trk_map[168];
|
|
uint32_t spd_map[168];
|
|
int *sector_map;
|
|
};
|
|
|
|
static img_info gcr_info[16] = {};
|
|
|
|
static uint8_t trk_buf[8192];
|
|
static uint8_t gcr_buf[G64_MAX_TRACK_LEN*2];
|
|
static uint8_t track_count[4] = {35, 40, 42, 70};
|
|
static int start_sectors[4][85] = {
|
|
{ // single sided, 35 tracks
|
|
0, 21, 42, 63, 84, 105, 126, 147, 168, 189, 210, 231, 252, 273, 294, 315, 336, 357, 376, 395, 414,
|
|
433, 452, 471, 490, 508, 526, 544, 562, 580, 598, 615, 632, 649, 666, 683, 683, 683, 683, 683, 683, 683,
|
|
683, 683, 683, 683, 683, 683, 683, 683, 683, 683, 683, 683, 683, 683, 683, 683, 683, 683, 683, 683, 683,
|
|
683, 683, 683, 683, 683, 683, 683, 683, 683, 683, 683, 683, 683, 683, 683, 683, 683, 683, 683, 683, 683,
|
|
683
|
|
},
|
|
{ // single sided, 40 tracks
|
|
0, 21, 42, 63, 84, 105, 126, 147, 168, 189, 210, 231, 252, 273, 294, 315, 336, 357, 376, 395, 414,
|
|
433, 452, 471, 490, 508, 526, 544, 562, 580, 598, 615, 632, 649, 666, 683, 700, 717, 734, 751, 768, 768,
|
|
768, 768, 768, 768, 768, 768, 768, 768, 768, 768, 768, 768, 768, 768, 768, 768, 768, 768, 768, 768, 768,
|
|
768, 768, 768, 768, 768, 768, 768, 768, 768, 768, 768, 768, 768, 768, 768, 768, 768, 768, 768, 768, 768,
|
|
768
|
|
},
|
|
{ // single sided, 42 tracks
|
|
0, 21, 42, 63, 84, 105, 126, 147, 168, 189, 210, 231, 252, 273, 294, 315, 336, 357, 376, 395, 414,
|
|
433, 452, 471, 490, 508, 526, 544, 562, 580, 598, 615, 632, 649, 666, 683, 700, 717, 734, 751, 768, 785,
|
|
802, 802, 802, 802, 802, 802, 802, 802, 802, 802, 802, 802, 802, 802, 802, 802, 802, 802, 802, 802, 802,
|
|
802, 802, 802, 802, 802, 802, 802, 802, 802, 802, 802, 802, 802, 802, 802, 802, 802, 802, 802, 802, 802,
|
|
802,
|
|
},
|
|
{ // double sided, 35 tracks per side
|
|
0, 21, 42, 63, 84, 105, 126, 147, 168, 189, 210, 231, 252, 273, 294, 315, 336, 357, 376, 395, 414,
|
|
433, 452, 471, 490, 508, 526, 544, 562, 580, 598, 615, 632, 649, 666, 683, 683, 683, 683, 683, 683, 683,
|
|
683, 704, 725, 746, 767, 788, 809, 830, 851, 872, 893, 914, 935, 956, 977, 998, 1019, 1040, 1059, 1078, 1097,
|
|
1116, 1135, 1154, 1173, 1191, 1209, 1227, 1245, 1263, 1281, 1298, 1315, 1332, 1349, 1366, 1366, 1366, 1366, 1366, 1366, 1366,
|
|
1366
|
|
}
|
|
// { // double sided, 40 tracks per side
|
|
// 0, 21, 42, 63, 84, 105, 126, 147, 168, 189, 210, 231, 252, 273, 294, 315, 336, 357, 376, 395, 414,
|
|
// 433, 452, 471, 490, 508, 526, 544, 562, 580, 598, 615, 632, 649, 666, 683, 700, 717, 734, 751, 768, 768,
|
|
// 768, 789, 810, 831, 852, 873, 894, 915, 936, 957, 978, 999, 1020, 1041, 1062, 1083, 1104, 1125, 1144, 1163, 1182,
|
|
// 1201, 1220, 1239, 1258, 1276, 1294, 1312, 1330, 1348, 1366, 1383, 1400, 1417, 1434, 1451, 1468, 1485, 1502, 1519, 1536, 1536,
|
|
// 1536
|
|
// },
|
|
// { // double sided, 42 tracks per side
|
|
// 0, 21, 42, 63, 84, 105, 126, 147, 168, 189, 210, 231, 252, 273, 294, 315, 336, 357, 376, 395, 414,
|
|
// 433, 452, 471, 490, 508, 526, 544, 562, 580, 598, 615, 632, 649, 666, 683, 700, 717, 734, 751, 768, 785,
|
|
// 802, 823, 844, 865, 886, 907, 928, 949, 970, 991, 1012, 1033, 1054, 1075, 1096, 1117, 1138, 1159, 1178, 1197, 1216,
|
|
// 1235, 1254, 1273, 1292, 1310, 1328, 1346, 1364, 1382, 1400, 1417, 1434, 1451, 1468, 1485, 1502, 1519, 1536, 1553, 1570, 1587,
|
|
// 1587
|
|
// }
|
|
};
|
|
|
|
int c64_openGCR(const char *path, fileTYPE *f, int idx)
|
|
{
|
|
// Return value:
|
|
//
|
|
// negative value:
|
|
// error
|
|
//
|
|
// positive value:
|
|
// bit 0=dual sided (G64_SUPPORT_DS)
|
|
// 1=raw GCR supported (G64_SUPPORT_GCR)
|
|
// 2=raw MFM supported (G64_SUPPORT_MFM)
|
|
|
|
gcr_info[idx].f = f;
|
|
if (!strcasecmp(path + strlen(path) - 4, ".g64") || !strcasecmp(path + strlen(path) - 4, ".g71"))
|
|
{
|
|
char str[16];
|
|
FileReadAdv(f, str, 12);
|
|
if (memcmp(str, "GCR-1541", 8) == 0 && memcmp(str, "GCR-1571", 8) == 0)
|
|
{
|
|
printf("Not valid G64 or G71 format: missing marker\n");
|
|
return -1;
|
|
}
|
|
|
|
gcr_info[idx].type = 2;
|
|
gcr_info[idx].tracks = str[9];
|
|
gcr_info[idx].sector_map = 0;
|
|
if (gcr_info[idx].tracks != 84 && gcr_info[idx].tracks != 168)
|
|
{
|
|
gcr_info[idx].type = 0;
|
|
printf("Not valid G64 or G71 format: invalid track count %d\n", gcr_info[idx].tracks);
|
|
return -1;
|
|
}
|
|
memset(gcr_info[idx].trk_map, 0, sizeof(gcr_info[idx].trk_map));
|
|
FileReadAdv(f, gcr_info[idx].trk_map, gcr_info[idx].tracks*4);
|
|
memset(gcr_info[idx].spd_map, 0, sizeof(gcr_info[idx].spd_map));
|
|
FileReadAdv(f, gcr_info[idx].spd_map, gcr_info[idx].tracks*4);
|
|
printf("G64/G71 disk tracks=%d\n", gcr_info[idx].tracks);
|
|
|
|
return G64_SUPPORT_GCR | G64_SUPPORT_MFM | (gcr_info[idx].tracks > 84 ? G64_SUPPORT_DS : 0);
|
|
}
|
|
else
|
|
{
|
|
gcr_info[idx].type = 1;
|
|
for (int i = 0; i < 4 ; i++) {
|
|
gcr_info[idx].tracks = track_count[i];
|
|
gcr_info[idx].sector_map = &start_sectors[i][0];
|
|
if (f->size <= gcr_info[idx].sector_map[84] * 257) break;
|
|
}
|
|
FileSeek(f, 0x165a2, SEEK_SET);
|
|
gcr_info[idx].id[0] = 0;
|
|
gcr_info[idx].id[1] = 0;
|
|
FileReadAdv(f, gcr_info[idx].id, 2);
|
|
printf("D64/D71 disk id1=%02X, id2=%02X, tracks=%d, sectors=%d\n", gcr_info[idx].id[0], gcr_info[idx].id[1], gcr_info[idx].tracks, gcr_info[idx].sector_map[84]);
|
|
|
|
return G64_SUPPORT_GCR | (gcr_info[idx].tracks > 42 ? G64_SUPPORT_DS : 0);
|
|
}
|
|
}
|
|
|
|
void c64_closeGCR(int idx)
|
|
{
|
|
gcr_info[idx].type = 0;
|
|
}
|
|
|
|
static const uint8_t gcr_lut[16] = {
|
|
0x0a, 0x0b, 0x12, 0x13,
|
|
0x0e, 0x0f, 0x16, 0x17,
|
|
0x09, 0x19, 0x1a, 0x1b,
|
|
0x0d, 0x1d, 0x1e, 0x15
|
|
};
|
|
|
|
static const uint8_t bin_lut[32] = {
|
|
0, 0, 0, 0, 0, 0, 0, 0,
|
|
0, 8, 0, 1, 0, 12, 4, 5,
|
|
0, 0, 2, 3, 0, 15, 6, 7,
|
|
0, 9, 10, 11, 0, 13, 14, 0
|
|
};
|
|
|
|
static uint8_t *gcrptr;
|
|
static int gcrcnt = 0;
|
|
|
|
void bin2gcr(uint8_t bin)
|
|
{
|
|
static uint64_t gcr = 0;
|
|
|
|
gcr <<= 5;
|
|
gcr |= gcr_lut[(bin >> 4) & 0xF];
|
|
gcr <<= 5;
|
|
gcr |= gcr_lut[bin & 0xF];
|
|
|
|
gcrcnt++;
|
|
if (gcrcnt == 4)
|
|
{
|
|
gcrcnt = 0;
|
|
*gcrptr++ = (uint8_t)(gcr >> 32);
|
|
*gcrptr++ = (uint8_t)(gcr >> 24);
|
|
*gcrptr++ = (uint8_t)(gcr >> 16);
|
|
*gcrptr++ = (uint8_t)(gcr >> 8);
|
|
*gcrptr++ = (uint8_t)(gcr);
|
|
}
|
|
}
|
|
|
|
void gcr2bin(uint8_t *gcr, uint8_t *bin)
|
|
{
|
|
// from VICE
|
|
register uint32_t tmp = *gcr;
|
|
tmp <<= 13;
|
|
|
|
for (int i = 5; i < 13; i += 2, bin++)
|
|
{
|
|
gcr++;
|
|
tmp |= ((uint32_t)(*gcr)) << i;
|
|
*bin = bin_lut[(tmp >> 16) & 0x1f] << 4;
|
|
tmp <<= 5;
|
|
*bin |= bin_lut[(tmp >> 16) & 0x1f];
|
|
tmp <<= 5;
|
|
}
|
|
}
|
|
|
|
void c64_readGCR(int idx, uint64_t lba, uint32_t blks)
|
|
{
|
|
// dbgprintf("c64_readGCR: idx=%d, lba=%04llx, blks=%d\n", idx, lba, blks);
|
|
bool is_1571 = (lba & 0x400) != 0;
|
|
|
|
uint8_t track = (uint8_t)lba;
|
|
uint8_t track_f = track >> 1;
|
|
|
|
uint32_t track_size;
|
|
|
|
if (!gcr_info[idx].type) return;
|
|
|
|
if (gcr_info[idx].type == 2)
|
|
{
|
|
if (track >= gcr_info[idx].tracks || !gcr_info[idx].trk_map[track])
|
|
{
|
|
track_size = 0;
|
|
dbgprintf("Track %d%s: no data, size %d\n", (track >> 1) + 1, (track & 1) ? ".5" : "", track_size);
|
|
}
|
|
else
|
|
{
|
|
FileSeek(gcr_info[idx].f, gcr_info[idx].trk_map[track], SEEK_SET);
|
|
FileReadAdv(gcr_info[idx].f, gcr_buf, blks * 256);
|
|
track_size = (gcr_buf[1] << 8) | gcr_buf[0];
|
|
dbgprintf("Track %d%s: read ok, size %d\n", (track >> 1) + 1, (track & 1) ? ".5" : "", track_size);
|
|
}
|
|
}
|
|
else if (track & 1)
|
|
{
|
|
track_size = 0;
|
|
dbgprintf("\nBetween tracks %d <|> %d.\n", track_f, track_f+1);
|
|
}
|
|
else
|
|
{
|
|
uint8_t track_h = ((track_f >= 42) ? track_f%42 + gcr_info[idx].tracks/2 : track_f) + 1;
|
|
int size = track_f < 84 ? (gcr_info[idx].sector_map[track_f + 1] - gcr_info[idx].sector_map[track_f]) * 256 : 0;
|
|
|
|
// dbgprintf("GCR physical track=%d%s, logical track=%d, size=%d\n", (track >> 1) + 1, (track & 1) ? ".5" : "", track_h, size);
|
|
if (size) {
|
|
FileSeek(gcr_info[idx].f, gcr_info[idx].sector_map[track_f] * 256, SEEK_SET);
|
|
FileReadAdv(gcr_info[idx].f, trk_buf, size);
|
|
|
|
uint8_t sec = 0;
|
|
gcrptr = gcr_buf + 2;
|
|
for (int ptr = 0; ptr < size; ptr += 256)
|
|
{
|
|
gcrcnt = 0;
|
|
*gcrptr++ = 0xFF; *gcrptr++ = 0xFF; *gcrptr++ = 0xFF; *gcrptr++ = 0xFF; *gcrptr++ = 0xFF;
|
|
bin2gcr(0x08);
|
|
bin2gcr(sec ^ track_h ^ gcr_info[idx].id[0] ^ gcr_info[idx].id[1]);
|
|
bin2gcr(sec);
|
|
bin2gcr(track_h);
|
|
bin2gcr(gcr_info[idx].id[1]);
|
|
bin2gcr(gcr_info[idx].id[0]);
|
|
bin2gcr(0x0F);
|
|
bin2gcr(0x0F);
|
|
*gcrptr++ = 0x55; *gcrptr++ = 0x55; *gcrptr++ = 0x55; *gcrptr++ = 0x55; *gcrptr++ = 0x55;
|
|
*gcrptr++ = 0x55; *gcrptr++ = 0x55; *gcrptr++ = 0x55; *gcrptr++ = 0x55;
|
|
|
|
uint8_t cs = 0;
|
|
uint8_t bt;
|
|
|
|
*gcrptr++ = 0xFF; *gcrptr++ = 0xFF; *gcrptr++ = 0xFF; *gcrptr++ = 0xFF; *gcrptr++ = 0xFF;
|
|
bin2gcr(0x07);
|
|
for (int i = 0; i < 256; i++)
|
|
{
|
|
bt = trk_buf[ptr + i];
|
|
cs ^= bt;
|
|
bin2gcr(bt);
|
|
}
|
|
bin2gcr(cs);
|
|
bin2gcr(0);
|
|
bin2gcr(0);
|
|
|
|
int gap = (track_h < 18) ? 8 : (track_h < 25) ? 17 : (track_h < 31) ? 12 : 9;
|
|
while (gap--) *gcrptr++ = 0x55;
|
|
sec++;
|
|
}
|
|
|
|
track_size = gcrptr - gcr_buf - 2;
|
|
dbgprintf("Read GCR track %d: bin_size = %d, gcr_size = %d\n", track_f+1, size, track_size);
|
|
}
|
|
else {
|
|
track_size = 0;
|
|
dbgprintf("Read non-existant GCR track %d: bin_size = %d, gcr_size = %d\n", track_f+1, size, track_size);
|
|
}
|
|
}
|
|
|
|
if (track_size > (blks * 256) - 2)
|
|
track_size = (blks * 256) - 2;
|
|
|
|
uint32_t buffer_size = track_size + 2;
|
|
if (track_size == 0) {
|
|
if (is_1571) {
|
|
buffer_size = 12502U;
|
|
}
|
|
else {
|
|
#ifdef EXTEND_1541
|
|
track_size = (track_h < 18) ? 7692U : (track_h < 25) ? 7142U : (track_h < 31) ? 6666U : 6250U;
|
|
#else
|
|
track_size = 4096;
|
|
#endif
|
|
buffer_size = track_size + 2;
|
|
}
|
|
memset(gcr_buf, 0, buffer_size);
|
|
}
|
|
|
|
gcr_buf[0] = (uint8_t)track_size;
|
|
gcr_buf[1] = (uint8_t)(track_size >> 8);
|
|
|
|
EnableIO();
|
|
spi_w(UIO_SECTOR_RD | (idx << 8));
|
|
spi_block_write(gcr_buf, user_io_get_width(), buffer_size);
|
|
DisableIO();
|
|
}
|
|
|
|
static uint8_t* align(uint8_t* src, int size)
|
|
{
|
|
static uint8_t buf[512];
|
|
memcpy(buf, src, size);
|
|
|
|
int rol = 0;
|
|
while (buf[0] & 0x80)
|
|
{
|
|
rol++;
|
|
uint8_t c = 0, t;
|
|
for (int i = size - 1; i >= 0; i--)
|
|
{
|
|
t = buf[i] & 0x80;
|
|
buf[i] = (buf[i] << 1) | c;
|
|
c = t ? 1 : 0;
|
|
}
|
|
}
|
|
|
|
if (rol)
|
|
{
|
|
dbgprintf("** ROL = %d ** ", rol);
|
|
}
|
|
return buf;
|
|
}
|
|
|
|
uint32_t c64_get_track_speed(int idx, uint64_t lba, uint32_t track_size){
|
|
uint32_t freq;
|
|
uint8_t track = (uint8_t)lba;
|
|
|
|
if (track_size > G64_TRACK_SPACE_GCR) {
|
|
freq = 8;
|
|
dbgprintf("Track %d%s: freq=%d (mfm)\n", (track >> 1) + 1, (track & 1) ? ".5" : "", freq);
|
|
}
|
|
else if (lba & 0x400) {
|
|
freq = (lba & 0x300) >> 8;
|
|
dbgprintf("Track %d%s: freq=%d (gcr, provided)\n", (track >> 1) + 1, (track & 1) ? ".5" : "", freq);
|
|
}
|
|
else {
|
|
uint8_t track_f = track >> 1;
|
|
uint8_t track_h = ((track_f > 42) ? track_f%42 + gcr_info[idx].tracks/2 : track_f) + 1;
|
|
freq = (track_h < 18) ? 3U : (track_h < 25) ? 2U : (track_h < 31) ? 1U : 0U;
|
|
dbgprintf("Track %d%s: freq=%d (gcr, calculated)\n", (track >> 1) + 1, (track & 1) ? ".5" : "", freq);
|
|
}
|
|
|
|
return freq;
|
|
}
|
|
|
|
void c64_writeGCR(int idx, uint64_t lba, uint32_t blks)
|
|
{
|
|
// dbgprintf("c64_writeGCR: idx=%d, lba=%04llx, blks=%d\n", idx, lba, blks);
|
|
|
|
uint8_t track = (uint8_t)lba;
|
|
#ifdef EXTEND_1541
|
|
bool allow_new_track = true;
|
|
#else
|
|
bool allow_new_track = (lba & 0x400) != 0;
|
|
#endif
|
|
|
|
if (!gcr_info[idx].type) return;
|
|
|
|
static uint8_t sec_buf[260];
|
|
|
|
EnableIO();
|
|
spi_w(UIO_SECTOR_WR | (idx << 8));
|
|
spi_block_read(gcr_buf, user_io_get_width(), blks * 256);
|
|
DisableIO();
|
|
|
|
uint32_t track_size = (gcr_buf[1] << 8) | gcr_buf[0];
|
|
|
|
if (gcr_info[idx].type == 2)
|
|
{
|
|
if (track >= gcr_info[idx].tracks)
|
|
{
|
|
dbgprintf("Ignore track %d%s: out of range\n", (track >> 1) + 1, (track & 1) ? ".5" : "");
|
|
return;
|
|
}
|
|
|
|
uint32_t track_pos = gcr_info[idx].trk_map[track];
|
|
if (track_pos == 0 && !allow_new_track) {
|
|
return;
|
|
}
|
|
|
|
uint32_t track_end = 0;
|
|
uint32_t file_end = gcr_info[idx].f->size;
|
|
|
|
if (track_pos > 0) {
|
|
// find track end position
|
|
track_end = file_end;
|
|
for (uint8_t t=0; t<gcr_info[idx].tracks; t++)
|
|
{
|
|
if (gcr_info[idx].trk_map[t] > track_pos && gcr_info[idx].trk_map[t] < track_end)
|
|
track_end = gcr_info[idx].trk_map[t];
|
|
if (gcr_info[idx].spd_map[t] > track_pos && gcr_info[idx].spd_map[t] < track_end)
|
|
track_end = gcr_info[idx].spd_map[t];
|
|
}
|
|
}
|
|
|
|
uint32_t track_space = track_end - track_pos;
|
|
|
|
if (track_size + 2 > track_space) {
|
|
if (!allow_new_track) {
|
|
return;
|
|
}
|
|
|
|
// TODO reclaim unused space
|
|
if (track_space > 0) {
|
|
dbgprintf("Write Track %d%s: not enough space, relocating to end of file\n", (track >> 1) + 1, (track & 1) ? ".5" : "");
|
|
}
|
|
else {
|
|
dbgprintf("Write Track %d%s: new track, saving to end of file\n", (track >> 1) + 1, (track & 1) ? ".5" : "");
|
|
}
|
|
|
|
FileSeek(gcr_info[idx].f, 0, SEEK_END);
|
|
FileWriteAdv(gcr_info[idx].f, gcr_buf, track_size + 2);
|
|
|
|
// update track map entry
|
|
gcr_info[idx].trk_map[track] = file_end;
|
|
FileSeek(gcr_info[idx].f, 12+track*4, SEEK_SET);
|
|
FileWriteAdv(gcr_info[idx].f, &gcr_info[idx].trk_map[track], 4);
|
|
|
|
// update speed map entry
|
|
uint32_t spd = c64_get_track_speed(idx, lba, track_size);
|
|
if (gcr_info[idx].spd_map[track] != spd) {
|
|
gcr_info[idx].spd_map[track] = spd;
|
|
FileSeek(gcr_info[idx].f, 12+(gcr_info[idx].tracks+track)*4, SEEK_SET);
|
|
FileWriteAdv(gcr_info[idx].f, &gcr_info[idx].spd_map[track], 4);
|
|
}
|
|
|
|
if (track_space > 0) {
|
|
// clear old space
|
|
memset(gcr_buf, 0xff, track_space);
|
|
FileSeek(gcr_info[idx].f, track_pos, SEEK_SET);
|
|
FileWriteAdv(gcr_info[idx].f, gcr_buf, track_space);
|
|
}
|
|
|
|
FileSeek(gcr_info[idx].f, 0, SEEK_END);
|
|
track_space = (
|
|
(track_size <= G64_TRACK_SPACE_GCR) ? G64_TRACK_SPACE_GCR
|
|
: (track_size <= G64_TRACK_SPACE_MFM) ? G64_TRACK_SPACE_MFM : track_size
|
|
) + 2;
|
|
}
|
|
else {
|
|
FileSeek(gcr_info[idx].f, track_pos, SEEK_SET);
|
|
FileWriteAdv(gcr_info[idx].f, gcr_buf, track_size + 2);
|
|
}
|
|
|
|
// fill unused space
|
|
int unused = track_space - track_size - 2;
|
|
if (unused > 0) {
|
|
if (unused > G64_MAX_TRACK_LEN * 2) unused = G64_MAX_TRACK_LEN * 2;
|
|
memset(gcr_buf, 0xff, unused);
|
|
FileWriteAdv(gcr_info[idx].f, gcr_buf, unused);
|
|
}
|
|
|
|
dbgprintf("Write Track %d%s: size %d, unused %d\n", (track >> 1) + 1, (track & 1) ? ".5" : "", track_size, unused);
|
|
return;
|
|
}
|
|
|
|
if (track & 1)
|
|
{
|
|
dbgprintf("Discard data between tracks!\n");
|
|
return;
|
|
}
|
|
|
|
track >>= 1;
|
|
|
|
//hexdump(gcr_buf, 8192);
|
|
|
|
int sec_cnt = track < 84 ? gcr_info[idx].sector_map[track + 1] - gcr_info[idx].sector_map[track] : 0;
|
|
if (sec_cnt == 0)
|
|
{
|
|
dbgprintf("Ignore track %d: invalid\n", track+1);
|
|
return;
|
|
}
|
|
|
|
dbgprintf("\n\nGCR track = %d\n", track + 1);
|
|
|
|
int sync = 0;
|
|
uint8_t prev = 0, started = 0;
|
|
uint32_t off = 0, ptr = 2;
|
|
uint8_t sec = 0xFF;
|
|
|
|
memcpy(gcr_buf + track_size + 2, gcr_buf + 2, track_size);
|
|
memset(trk_buf, 0, sizeof(trk_buf));
|
|
|
|
while(ptr < track_size + 2)
|
|
{
|
|
if (prev == 0xFF && gcr_buf[ptr + off] == 0xFF)
|
|
{
|
|
sync = 1;
|
|
}
|
|
|
|
if (gcr_buf[ptr + off] != 0xFF && sync)
|
|
{
|
|
uint8_t *hdr = align(gcr_buf + ptr + off, 11);
|
|
|
|
uint32_t bin;
|
|
gcr2bin(hdr, (uint8_t*)&bin);
|
|
if (!started && (bin & 0xFF) == 8)
|
|
{
|
|
off = ptr - 2;
|
|
ptr = 2;
|
|
started = 1;
|
|
dbgprintf("Start at %d\n\n", off);
|
|
}
|
|
|
|
dbgprintf("Sync = %08X: ", bin);
|
|
|
|
if ((bin & 0xFF) == 8)
|
|
{
|
|
sec = (uint8_t)(bin >> 16);
|
|
gcr2bin(hdr + 5, (uint8_t*)&bin);
|
|
gcr_info[idx].id[1] = (uint8_t)(bin);
|
|
gcr_info[idx].id[0] = (uint8_t)(bin >> 8);
|
|
|
|
dbgprintf("sec = %d, id1 = %02X, id2 = %02X\n", sec, gcr_info[idx].id[0], gcr_info[idx].id[1]);
|
|
}
|
|
else if ((bin & 0xFF) == 7)
|
|
{
|
|
if (sec < sec_cnt)
|
|
{
|
|
dbgprintf("data...\n\n");
|
|
uint8_t *data = align(gcr_buf + ptr + off, 330);
|
|
|
|
int dst = 0;
|
|
int src = 0;
|
|
for (; dst < 260; src += 5, dst += 4)
|
|
{
|
|
gcr2bin(data + src, sec_buf + dst);
|
|
}
|
|
|
|
memcpy(trk_buf + (sec * 256), sec_buf + 1, 256);
|
|
/*
|
|
printf("Sec data:\n");
|
|
hexdump(trk_buf + (sec * 256), 256);
|
|
printf("\n");
|
|
*/
|
|
}
|
|
else
|
|
{
|
|
dbgprintf("nothing here.\n\n");
|
|
sec = 0xFF;
|
|
}
|
|
|
|
ptr += 256; // a little before the end
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
printf("\noff %X\n", ptr + off - 20);
|
|
hexdump(gcr_buf + ptr + off - 20, 256);
|
|
*/
|
|
}
|
|
sync = 0;
|
|
}
|
|
else
|
|
{
|
|
prev = gcr_buf[ptr + off];
|
|
ptr++;
|
|
}
|
|
}
|
|
|
|
FileSeek(gcr_info[idx].f, gcr_info[idx].sector_map[track] * 256, SEEK_SET);
|
|
FileWriteAdv(gcr_info[idx].f, trk_buf, sec_cnt * 256);
|
|
}
|
|
|
|
static const int crt_bank_size = 8192 + 16;
|
|
static const int ezfl_size = 64 + (128 * crt_bank_size);
|
|
static char *ezfl_save_name = 0;
|
|
static const uint8_t ezfl_shdr[16] = { 0x43, 0x48, 0x49, 0x50, 0x00, 0x00, 0x20, 0x10, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00 };
|
|
|
|
static char ezfl_hdr[64];
|
|
static char ezfl_buf[crt_bank_size];
|
|
|
|
static void ezfl_format(uint8_t *buf)
|
|
{
|
|
memset(buf, -1, ezfl_size);
|
|
memcpy(buf, ezfl_hdr, 64);
|
|
|
|
uint8_t bank = 0;
|
|
uint8_t addr = 0x80;
|
|
for (int off = 64; off < ezfl_size; off += crt_bank_size)
|
|
{
|
|
memcpy(buf + off, ezfl_shdr, 16);
|
|
buf[off + 11] = bank;
|
|
buf[off + 12] = addr;
|
|
addr ^= 0x20;
|
|
if (addr == 0x80) bank++;
|
|
}
|
|
}
|
|
|
|
static uint8_t* ezfl_load(const char* name)
|
|
{
|
|
printf("Loading %s.\n", name);
|
|
|
|
uint8_t *buf = new uint8_t[ezfl_size];
|
|
if (buf)
|
|
{
|
|
fileTYPE f = {};
|
|
if (FileOpen(&f, name) && f.size >= 64)
|
|
{
|
|
if (FileReadAdv(&f, ezfl_hdr, 64))
|
|
{
|
|
ezfl_format(buf);
|
|
int sz;
|
|
while ((sz = FileReadAdv(&f, ezfl_buf, crt_bank_size)))
|
|
{
|
|
while (sz < crt_bank_size) ezfl_buf[sz++] = 0xFF;
|
|
|
|
uint8_t bank = ezfl_buf[11];
|
|
uint8_t addr = ezfl_buf[12];
|
|
int off = 64 + (((bank * 2) + ((addr <= 0x80) ? 0 : 1)) * crt_bank_size);
|
|
if ((off+ crt_bank_size) <= ezfl_size) memcpy(buf + off, ezfl_buf, crt_bank_size);
|
|
else printf("WARNING: invalid Easyflash bank header (%d, 0x%02X), skipping.\n", bank, addr);
|
|
}
|
|
}
|
|
|
|
int len = strlen(f.name);
|
|
char *p = strrchr(f.name, '.');
|
|
if (!p) p = f.name + len;
|
|
user_io_file_info(p);
|
|
|
|
FileClose(&f);
|
|
return buf;
|
|
}
|
|
FileClose(&f);
|
|
delete[] buf;
|
|
}
|
|
|
|
printf("ERROR: cannot load %s.\n", name);
|
|
return 0;
|
|
}
|
|
|
|
static void ezfl_save(const char* name, uint8_t *buf)
|
|
{
|
|
printf("Saving easyflash to %s\n", name);
|
|
|
|
fileTYPE f = {};
|
|
if (FileOpenEx(&f, name, O_CREAT | O_TRUNC | O_RDWR | O_SYNC | O_CLOEXEC))
|
|
{
|
|
FileWriteAdv(&f, buf, 64);
|
|
|
|
int off = 64;
|
|
while (off < ezfl_size)
|
|
{
|
|
uint8_t chk = 0xFF;
|
|
for (int i = 16; i < crt_bank_size; i++) chk &= buf[off + i];
|
|
if (chk != 0xFF) FileWriteAdv(&f, buf + off, crt_bank_size);
|
|
off += crt_bank_size;
|
|
}
|
|
|
|
FileClose(&f);
|
|
}
|
|
else
|
|
{
|
|
printf("ERROR: cannot creat file %s.\n", name);
|
|
}
|
|
}
|
|
|
|
void c64_open_file(const char* name, unsigned char index)
|
|
{
|
|
int slen = strlen(name);
|
|
if (slen > 4 && !strcasecmp(name + slen - 4, ".crt"))
|
|
{
|
|
ezfl_save_name = 0;
|
|
static uint8_t buf[1024];
|
|
memset(buf, 0, sizeof(buf));
|
|
fileTYPE f = {};
|
|
if (FileOpen(&f, name))
|
|
{
|
|
FileReadAdv(&f, buf, 64);
|
|
FileClose(&f);
|
|
|
|
if (!buf[0x16] && (buf[0x17] == 32 || buf[0x17] == 33))
|
|
{
|
|
FileGenerateSavePath(name, (char*)buf);
|
|
ProgressMessage(0, 0, 0, 0);
|
|
|
|
int mod = get_key_mod();
|
|
printf("mod = %x\n", mod);
|
|
|
|
uint8_t *ezfl = ezfl_load((!(get_key_mod() & 0x2200) && FileExists((char*)buf)) ? (char*)buf : name);
|
|
if (ezfl)
|
|
{
|
|
user_io_set_index(index);
|
|
user_io_set_download(1);
|
|
int off = 0;
|
|
while (off < ezfl_size)
|
|
{
|
|
int chunk = ezfl_size - off;
|
|
if (chunk > 4096) chunk = 4096;
|
|
|
|
ProgressMessage("Loading", f.name, off, ezfl_size);
|
|
user_io_file_tx_data(ezfl + off, chunk);
|
|
off += chunk;
|
|
}
|
|
user_io_set_download(0);
|
|
ProgressMessage(0, 0, 0, 0);
|
|
}
|
|
|
|
delete[] ezfl;
|
|
FileGenerateSavePath(name, (char*)ezfl_buf);
|
|
ezfl_save_name = (char *)ezfl_buf;
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
user_io_file_tx(name, index);
|
|
}
|
|
|
|
void c64_save_cart(int open_menu)
|
|
{
|
|
if (ezfl_save_name)
|
|
{
|
|
uint8_t *ezfl = new uint8_t[ezfl_size];
|
|
if (ezfl)
|
|
{
|
|
ezfl_format(ezfl);
|
|
ProgressMessage(0, 0, 0, 0);
|
|
|
|
printf("Reading cartrridge from SDRAM...\n");
|
|
|
|
int off = 64 + 16;
|
|
user_io_set_index(99);
|
|
user_io_set_upload(1);
|
|
while (off < ezfl_size)
|
|
{
|
|
ProgressMessage("Saving", ezfl_save_name, off, ezfl_size);
|
|
user_io_file_rx_data(ezfl + off, 8192);
|
|
off += 8192 + 16;
|
|
}
|
|
user_io_set_upload(0);
|
|
|
|
ezfl_save(ezfl_save_name, ezfl);
|
|
ProgressMessage(0, 0, 0, 0);
|
|
delete[] ezfl;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
printf("ERROR: missing easyflash dump!\n");
|
|
}
|
|
|
|
if (open_menu) menu_key_set(KEY_F12);
|
|
}
|