diff --git a/projects/tzpuPico/tools/MZFD/MZFDTool b/projects/tzpuPico/tools/MZFD/MZFDTool new file mode 100755 index 0000000..8c9b997 Binary files /dev/null and b/projects/tzpuPico/tools/MZFD/MZFDTool differ diff --git a/projects/tzpuPico/tools/MZFD/MZFDTool.c b/projects/tzpuPico/tools/MZFD/MZFDTool.c new file mode 100755 index 0000000..652d77a --- /dev/null +++ b/projects/tzpuPico/tools/MZFD/MZFDTool.c @@ -0,0 +1,618 @@ +/* ========================================================================= + * MZFDTool — Sharp MZ-700 Floppy Disk image tool + * + * Creates and manages raw floppy disk images for the MZ-700 FDC system. + * Supports format, directory listing, adding/extracting MZF files, and + * setting a boot program. + * + * Original V1.01 (c) 2002 by BKK (DOS BIOS int13h version) + * Updated V2.00 (c) 2026 Philip Smart — rewritten for Linux with raw + * image file I/O, proper directory management, MZF conversion, + * and robust error handling. + * + * MZ-700 Floppy Disk format (327,680 bytes raw image): + * + * Geometry: 40 cylinders, 2 heads, 16 sectors/track, 256 bytes/sector. + * Data on disk is INVERTED (XOR 0xFF) due to MZ-700 hardware. + * + * Logical sector = (cylinder * 2 + head) * 16 + (phys_sector - 1) + * Total sectors: 40 * 2 * 16 = 1280 + * + * Boot sector: Logical sector 0 (Cyl 0, Head 0, Sector 1) + * Directory: Logical sectors 16-31 (Cyl 0, Head 1, Sectors 1-16) + * 16 sectors * 8 entries/sector = 128 entries + * Entry 0 of sector 16 = volume/FAT header + * Data area: Logical sector 32+ (Cyl 1+) + * + * Boot sector (32 bytes used): + * +00: Type (03 = MZ-700 bootable) + * +01: Signature "IPLPRO" (6 bytes) + * +07: Program name (11 bytes, CR-terminated) + * +12: Load address (2 bytes LE) + * +14: File size (2 bytes LE) + * +16: Reserved (8 bytes) + * +1E: Start data sector (2 bytes LE) + * + * Directory entry (32 bytes): + * +00: FileType (01=OBJ, 02=BTX, 00=deleted, >=80h=header) + * +01: FileName (17 bytes, CR-terminated, space-padded) + * +12: LockFlag (1 byte) + * +13: DummyFlag (1 byte) + * +14: FileSize (2 bytes LE, in bytes) + * +16: LoadAddress (2 bytes LE) + * +18: ExecAddress (2 bytes LE) + * +1A: Reserved (4 bytes) + * +1E: SectorAddress (2 bytes LE, logical) + * + * Usage: + * MZFDTool format [-o disk.img] Format empty disk image + * MZFDTool dir [-o disk.img] List directory + * MZFDTool add [-o disk.img] Add MZF file to directory + * MZFDTool extract [-o disk.img] Extract file to MZF + * MZFDTool boot [-o disk.img] Set boot program + * ========================================================================= */ + +#define VERSION "2.00" + +#include +#include +#include +#include +#include + +#define CYLS 40 +#define HEADS 2 +#define SECTORS 16 +#define SECSIZE 256 +#define TOTAL_SECTORS (CYLS * HEADS * SECTORS) +#define IMGSIZE (TOTAL_SECTORS * SECSIZE) +#define DIR_FIRST_SEC 16 /* First directory logical sector */ +#define DIR_SECTORS 16 /* Number of directory sectors */ +#define DIR_ENTRIES 8 /* Entries per sector */ +#define DATA_FIRST_SEC 32 /* First data logical sector */ +#define MAXFILETYPES 12 +#define DEFAULTIMAGE "MZ700.img" +#define CMTHDRSIZE 128 /* MZF/CMT file header size */ +#define BOOT_SIG_TYPE 3 /* MZ-700 boot type */ +#define BOOT_SIG "IPLPRO" +#define BOOT_SIG_LEN 6 + +#pragma pack(push, 1) + +/* Boot sector structure (first 32 bytes of logical sector 0) */ +struct BootSector { + uint8_t type; /* +00: 03 = MZ-700 bootable */ + char signature[6]; /* +01: "IPLPRO" */ + char name[11]; /* +07: Program name (CR-terminated) */ + uint16_t loadAddress; /* +12: Load address */ + uint16_t fileSize; /* +14: File size in bytes */ + uint8_t reserved[8]; /* +16: Reserved */ + uint16_t sectorAddress; /* +1E: Start data sector */ +}; + +/* Directory entry (32 bytes) */ +struct DirEntry { + uint8_t fileType; /* +00: 01=OBJ, 02=BTX, 00=deleted */ + char fileName[17]; /* +01: CR-terminated, space-padded */ + uint8_t lockFlag; /* +12: Lock flag */ + uint8_t dummyFlag; /* +13: Reserved */ + uint16_t fileSize; /* +14: File size in bytes */ + uint16_t loadAddress; /* +16: Load address */ + uint16_t execAddress; /* +18: Exec address */ + uint8_t dummy[4]; /* +1A: Reserved */ + uint16_t sectorAddress; /* +1E: Start sector (logical) */ +}; + +/* MZF/CMT tape file header (128 bytes) */ +struct CMTHeader { + uint8_t attribute; /* File type: 01=OBJ, 02=BTX, etc. */ + char name[17]; /* Filename (CR-terminated) */ + uint16_t size; /* Data size */ + uint16_t loadAddress; /* Load address */ + uint16_t execAddress; /* Execution address */ + char comment[104]; /* Comment area */ +}; + +#pragma pack(pop) + +static const char *FileTypes[MAXFILETYPES] = { + "???", "OBJ", "BTX", "BSD", "BRD", "RB ", + "???", "LIB", "???", "???", "SYS", "GR " +}; + +static uint8_t diskImage[IMGSIZE]; +static char imgFileName[256] = DEFAULTIMAGE; + +/* ------- Sector I/O helpers ------- */ + +/* Convert logical sector to byte offset in raw image */ +static int sec_offset(int logical_sec) +{ + /* Logical sector mapping: + * phys_sector = logical % 16 + 1 (but stored 0-based in image) + * head = (logical / 16) % 2 + * cylinder = logical / 16 / 2 + * Raw image is sequential: cyl0/head0/sec1..16, cyl0/head1/sec1..16, ... */ + return logical_sec * SECSIZE; +} + +/* Invert a buffer (XOR 0xFF) — MZ-700 data bus inversion */ +static void invert_buf(uint8_t *buf, int len) +{ + for (int i = 0; i < len; i++) + buf[i] ^= 0xFF; +} + +/* Read a logical sector from image into buffer (un-inverted) */ +static void read_sector(int logical_sec, uint8_t *buf) +{ + memcpy(buf, &diskImage[sec_offset(logical_sec)], SECSIZE); + invert_buf(buf, SECSIZE); +} + +/* Write a buffer to a logical sector in image (inverts before writing) */ +static void write_sector(int logical_sec, const uint8_t *buf) +{ + memcpy(&diskImage[sec_offset(logical_sec)], buf, SECSIZE); + invert_buf(&diskImage[sec_offset(logical_sec)], SECSIZE); +} + +/* Load disk image from file */ +static int load_image(void) +{ + FILE *f = fopen(imgFileName, "rb"); + if (!f) { + fprintf(stderr, "ERROR: Cannot open '%s'\n", imgFileName); + return 0; + } + size_t n = fread(diskImage, 1, IMGSIZE, f); + fclose(f); + if (n != IMGSIZE) { + fprintf(stderr, "ERROR: '%s' is not a valid disk image (%zu bytes, expected %d)\n", + imgFileName, n, IMGSIZE); + return 0; + } + return 1; +} + +/* Save disk image to file */ +static int save_image(void) +{ + FILE *f = fopen(imgFileName, "wb"); + if (!f) { + fprintf(stderr, "ERROR: Cannot write '%s'\n", imgFileName); + return 0; + } + fwrite(diskImage, IMGSIZE, 1, f); + fclose(f); + return 1; +} + +/* Format a filename for display: stop at CR, null-terminate */ +static void format_name(char *dst, const char *src, int maxlen) +{ + memcpy(dst, src, maxlen); + dst[maxlen] = '\0'; + for (int i = 0; i < maxlen; i++) { + if (dst[i] == '\r' || dst[i] == '\0') { dst[i] = '\0'; break; } + } +} + +/* ------- Commands ------- */ + +/* Format: create an empty formatted disk image */ +static void cmd_format(void) +{ + uint8_t sec[SECSIZE]; + + printf("Formatting '%s' (%d bytes, %d sectors)\n", imgFileName, IMGSIZE, TOTAL_SECTORS); + + /* Fill entire image with 0xFF (which inverts to 0x00 = empty) */ + memset(diskImage, 0xFF, IMGSIZE); + + /* Write volume header in directory sector 16, entry 0 */ + memset(sec, 0, SECSIZE); + sec[0] = 0x81; /* Volume header marker */ + sec[1] = 'O'; /* Volume type */ + /* +14: capacity hint = 100 (not used by RFS) */ + sec[0x14] = 100; + /* +1E: next free sector (first data sector) */ + sec[0x1E] = DATA_FIRST_SEC & 0xFF; + sec[0x1F] = DATA_FIRST_SEC >> 8; + write_sector(DIR_FIRST_SEC, sec); + + if (!save_image()) exit(1); + puts("Done."); +} + +/* Dir: list directory contents */ +static void cmd_dir(void) +{ + uint8_t sec[SECSIZE]; + char name[18]; + int files = 0; + + if (!load_image()) exit(1); + + /* Check boot sector */ + read_sector(0, sec); + struct BootSector *boot = (struct BootSector *)sec; + if (boot->type == BOOT_SIG_TYPE && memcmp(boot->signature, BOOT_SIG, BOOT_SIG_LEN) == 0) { + format_name(name, boot->name, 11); + printf("\nBoot program: %-17s Size=%u Load=0x%04X Sector=%u\n", + name, boot->fileSize, boot->loadAddress, boot->sectorAddress); + } else { + printf("\nDisk is not bootable.\n"); + } + + printf("\nDirectory of '%s':\n\n", imgFileName); + printf(" %-17s %-3s %5s %s %4s %4s %6s (C H S)\n", + "Name", "Typ", "Size", "L", "Load", "Exec", "Sector"); + printf(" %-17s %-3s %5s %s %4s %4s %6s %s\n", + "-----------------", "---", "-----", "-", "----", "----", "------", "-------"); + + for (int ds = 0; ds < DIR_SECTORS; ds++) { + read_sector(DIR_FIRST_SEC + ds, sec); + + for (int e = 0; e < DIR_ENTRIES; e++) { + /* Skip entry 0 of first directory sector (volume header) */ + if (ds == 0 && e == 0) continue; + + struct DirEntry *ent = (struct DirEntry *)&sec[e * sizeof(struct DirEntry)]; + if (ent->fileType == 0x00 || ent->fileType >= 0x80) + continue; + + uint8_t ft = ent->fileType; + if (ft >= MAXFILETYPES) ft = 0; + format_name(name, ent->fileName, 17); + + int ls = ent->sectorAddress; + int ps = ls % 16 + 1; + int hd = (ls / 16) % 2; + int cy = ls / 16 / 2; + + printf(" %-17s %s %5u %c %04X %04X %5u (%2d %d %2d)\n", + name, FileTypes[ft], ent->fileSize, + ent->lockFlag ? 'X' : ' ', + ent->loadAddress, ent->execAddress, + ent->sectorAddress, cy, hd, ps); + files++; + } + } + + /* Calculate free space */ + int highSec = DATA_FIRST_SEC; + for (int ds = 0; ds < DIR_SECTORS; ds++) { + read_sector(DIR_FIRST_SEC + ds, sec); + for (int e = 0; e < DIR_ENTRIES; e++) { + struct DirEntry *ent = (struct DirEntry *)&sec[e * sizeof(struct DirEntry)]; + if (ent->fileType > 0 && ent->fileType < 0x80 && ent->sectorAddress > 0) { + int endSec = ent->sectorAddress + (ent->fileSize + SECSIZE - 1) / SECSIZE; + if (endSec > highSec) highSec = endSec; + } + } + } + printf("\n%d file(s), %d sectors used, %d sectors free\n", + files, highSec, TOTAL_SECTORS - highSec); +} + +/* Add: add an MZF file to the disk directory */ +static void cmd_add(const char *mzfFileName) +{ + FILE *mzfFile; + uint8_t sec[SECSIZE]; + struct CMTHeader cmtHdr; + char name[18]; + + if (!load_image()) exit(1); + + /* Open and read MZF file */ + mzfFile = fopen(mzfFileName, "rb"); + if (!mzfFile) { + fprintf(stderr, "ERROR: Cannot open '%s'\n", mzfFileName); + exit(1); + } + fseek(mzfFile, 0, SEEK_END); + long mzfSize = ftell(mzfFile); + fseek(mzfFile, 0, SEEK_SET); + + if (mzfSize < CMTHDRSIZE) { + fprintf(stderr, "ERROR: '%s' too small for MZF format (%ld bytes)\n", mzfFileName, mzfSize); + fclose(mzfFile); + exit(1); + } + fread(&cmtHdr, CMTHDRSIZE, 1, mzfFile); + uint16_t dataSize = cmtHdr.size; + if (dataSize == 0) dataSize = (uint16_t)(mzfSize - CMTHDRSIZE); + + /* Find free directory entry and highest used sector */ + int freeDirSec = -1, freeDirIdx = -1; + int highSec = DATA_FIRST_SEC; + + for (int ds = 0; ds < DIR_SECTORS; ds++) { + read_sector(DIR_FIRST_SEC + ds, sec); + for (int e = 0; e < DIR_ENTRIES; e++) { + if (ds == 0 && e == 0) continue; /* Skip volume header */ + struct DirEntry *ent = (struct DirEntry *)&sec[e * sizeof(struct DirEntry)]; + + if (ent->fileType == 0x00 && freeDirSec < 0) { + freeDirSec = ds; + freeDirIdx = e; + } + if (ent->fileType > 0 && ent->fileType < 0x80 && ent->sectorAddress > 0) { + int endSec = ent->sectorAddress + (ent->fileSize + SECSIZE - 1) / SECSIZE; + if (endSec > highSec) highSec = endSec; + } + } + } + + if (freeDirSec < 0) { + fprintf(stderr, "ERROR: Directory full\n"); + fclose(mzfFile); + exit(1); + } + + int dataSectors = (dataSize + SECSIZE - 1) / SECSIZE; + if (highSec + dataSectors > TOTAL_SECTORS) { + fprintf(stderr, "ERROR: Not enough space (need %d sectors, have %d)\n", + dataSectors, TOTAL_SECTORS - highSec); + fclose(mzfFile); + exit(1); + } + + /* Write file data sectors */ + for (int s = 0; s < dataSectors; s++) { + memset(sec, 0, SECSIZE); + fread(sec, 1, SECSIZE, mzfFile); + write_sector(highSec + s, sec); + } + fclose(mzfFile); + + /* Update directory entry */ + read_sector(DIR_FIRST_SEC + freeDirSec, sec); + struct DirEntry *ent = (struct DirEntry *)&sec[freeDirIdx * sizeof(struct DirEntry)]; + memset(ent, 0, sizeof(struct DirEntry)); + + ent->fileType = cmtHdr.attribute; + if (ent->fileType == 0x05) ent->fileType = 0x02; /* CMT type 05 → FD type 02 (BTX) */ + memcpy(ent->fileName, cmtHdr.name, 17); + ent->fileSize = dataSize; + ent->loadAddress = cmtHdr.loadAddress; + ent->execAddress = cmtHdr.execAddress; + ent->sectorAddress = highSec; + write_sector(DIR_FIRST_SEC + freeDirSec, sec); + + format_name(name, cmtHdr.name, 17); + printf(" Added: \"%s\" type=%s size=%u load=0x%04X exec=0x%04X sector=%d\n", + name, FileTypes[ent->fileType < MAXFILETYPES ? ent->fileType : 0], + dataSize, cmtHdr.loadAddress, cmtHdr.execAddress, highSec); + printf(" %d sectors free\n", TOTAL_SECTORS - highSec - dataSectors); + + if (!save_image()) exit(1); +} + +/* Extract: extract a file from disk to MZF format */ +static void cmd_extract(const char *fileName) +{ + uint8_t sec[SECSIZE]; + char entName[18], outName[256]; + + if (!load_image()) exit(1); + + /* Search directory for matching filename */ + static struct DirEntry foundEntry; + int foundIt = 0; + + for (int ds = 0; ds < DIR_SECTORS && !foundIt; ds++) { + read_sector(DIR_FIRST_SEC + ds, sec); + for (int e = 0; e < DIR_ENTRIES && !foundIt; e++) { + if (ds == 0 && e == 0) continue; + struct DirEntry *ent = (struct DirEntry *)&sec[e * sizeof(struct DirEntry)]; + if (ent->fileType > 0 && ent->fileType < 0x80) { + format_name(entName, ent->fileName, 17); + if (strcmp(fileName, entName) == 0) { + memcpy(&foundEntry, ent, sizeof(foundEntry)); + foundIt = 1; + } + } + } + } + + if (!foundIt) { + /* Also check boot sector */ + read_sector(0, sec); + struct BootSector *boot = (struct BootSector *)sec; + if (boot->type == BOOT_SIG_TYPE && memcmp(boot->signature, BOOT_SIG, BOOT_SIG_LEN) == 0) { + format_name(entName, boot->name, 11); + if (strcmp(fileName, entName) == 0) { + memset(&foundEntry, 0, sizeof(foundEntry)); + foundEntry.fileType = 0x01; + memcpy(foundEntry.fileName, boot->name, 11); + foundEntry.fileSize = boot->fileSize; + foundEntry.loadAddress = boot->loadAddress; + foundEntry.sectorAddress = boot->sectorAddress; + foundIt = 1; + } + } + } + + if (!foundIt) { + fprintf(stderr, "ERROR: '%s' not found on disk\n", fileName); + exit(1); + } + + struct DirEntry *found = &foundEntry; + + /* Build output filename */ + snprintf(outName, sizeof(outName), "%s.mzf", fileName); + for (char *p = outName; *p; p++) { + if (*p == ' ') *p = '_'; + } + + /* Write MZF file */ + FILE *out = fopen(outName, "wb"); + if (!out) { + fprintf(stderr, "ERROR: Cannot create '%s'\n", outName); + exit(1); + } + + struct CMTHeader hdr; + memset(&hdr, 0, sizeof(hdr)); + hdr.attribute = (found->fileType == 0x02) ? 0x05 : found->fileType; + memcpy(hdr.name, found->fileName, 17); + hdr.size = found->fileSize; + hdr.loadAddress = found->loadAddress; + hdr.execAddress = found->execAddress; + snprintf(hdr.comment, sizeof(hdr.comment), "Extracted by MZFDTool V%s", VERSION); + fwrite(&hdr, sizeof(hdr), 1, out); + + /* Read and write data sectors */ + uint16_t remaining = found->fileSize; + int curSec = found->sectorAddress; + while (remaining > 0) { + read_sector(curSec, sec); + uint16_t chunk = (remaining > SECSIZE) ? SECSIZE : remaining; + fwrite(sec, 1, chunk, out); + remaining -= chunk; + curSec++; + } + + fclose(out); + format_name(entName, found->fileName, 17); + printf(" Extracted: \"%s\" → %s (%u bytes)\n", entName, outName, found->fileSize); +} + +/* Boot: set boot program from MZF file */ +static void cmd_boot(const char *mzfFileName) +{ + FILE *mzfFile; + uint8_t sec[SECSIZE]; + struct CMTHeader cmtHdr; + char name[18]; + + if (!load_image()) exit(1); + + mzfFile = fopen(mzfFileName, "rb"); + if (!mzfFile) { + fprintf(stderr, "ERROR: Cannot open '%s'\n", mzfFileName); + exit(1); + } + fseek(mzfFile, 0, SEEK_END); + long mzfSize = ftell(mzfFile); + fseek(mzfFile, 0, SEEK_SET); + + if (mzfSize < CMTHDRSIZE) { + fprintf(stderr, "ERROR: '%s' too small for MZF format\n", mzfFileName); + fclose(mzfFile); + exit(1); + } + fread(&cmtHdr, CMTHDRSIZE, 1, mzfFile); + uint16_t dataSize = cmtHdr.size; + if (dataSize == 0) dataSize = (uint16_t)(mzfSize - CMTHDRSIZE); + + /* Find space for boot data — use sectors 1-15 (logical), before directory */ + int dataSectors = (dataSize + SECSIZE - 1) / SECSIZE; + int bootDataStart = 1; /* Sector 1 (sector 0 is the boot sector itself) */ + if (dataSectors > 15) { + fprintf(stderr, "ERROR: Boot program too large (%d sectors, max 15)\n", dataSectors); + fclose(mzfFile); + exit(1); + } + + /* Write boot data to sectors 1-15 */ + for (int s = 0; s < dataSectors; s++) { + memset(sec, 0, SECSIZE); + fread(sec, 1, SECSIZE, mzfFile); + write_sector(bootDataStart + s, sec); + } + fclose(mzfFile); + + /* Write boot sector (sector 0) */ + memset(sec, 0, SECSIZE); + struct BootSector *boot = (struct BootSector *)sec; + boot->type = BOOT_SIG_TYPE; + memcpy(boot->signature, BOOT_SIG, BOOT_SIG_LEN); + format_name(name, cmtHdr.name, 11); + memcpy(boot->name, cmtHdr.name, 11); + boot->loadAddress = cmtHdr.loadAddress; + boot->fileSize = dataSize; + boot->sectorAddress = bootDataStart; + write_sector(0, sec); + + format_name(name, cmtHdr.name, 11); + printf(" Boot set: \"%s\" size=%u load=0x%04X exec=0x%04X sector=%d\n", + name, dataSize, cmtHdr.loadAddress, cmtHdr.execAddress, bootDataStart); + + if (!save_image()) exit(1); +} + +/* ------- Usage and main ------- */ + +static void usage(void) +{ + printf("Usage:\n"); + printf(" MZFDTool format [-o disk.img] Format empty disk image\n"); + printf(" MZFDTool dir [-o disk.img] List directory\n"); + printf(" MZFDTool add [-o disk.img] Add MZF file to disk\n"); + printf(" MZFDTool extract [-o disk.img] Extract file to MZF\n"); + printf(" MZFDTool boot [-o disk.img] Set boot program\n"); + printf("\n"); + printf("Options:\n"); + printf(" -o Disk image file (default: %s)\n", DEFAULTIMAGE); + printf("\n"); + printf("Disk geometry: %d cyls, %d heads, %d sectors, %d bytes/sector = %d bytes\n", + CYLS, HEADS, SECTORS, SECSIZE, IMGSIZE); + printf("\n"); +} + +int main(int argc, char *argv[]) +{ + printf("\nMZFDTool V%s (c) 2002 BKK, 2026 Philip Smart\n\n", VERSION); + + if (argc < 2) { + usage(); + return 1; + } + + /* Parse -o option from any position */ + for (int i = 1; i < argc - 1; i++) { + if (strcmp("-o", argv[i]) == 0) { + strncpy(imgFileName, argv[i + 1], sizeof(imgFileName) - 1); + imgFileName[sizeof(imgFileName) - 1] = '\0'; + for (int j = i; j < argc - 2; j++) + argv[j] = argv[j + 2]; + argc -= 2; + break; + } + } + + if (strcmp("format", argv[1]) == 0) { + cmd_format(); + } else if (strcmp("dir", argv[1]) == 0) { + cmd_dir(); + } else if (strcmp("add", argv[1]) == 0) { + if (argc < 3) { + fprintf(stderr, "ERROR: No MZF file specified\n"); + return 2; + } + cmd_add(argv[2]); + } else if (strcmp("extract", argv[1]) == 0) { + if (argc < 3) { + fprintf(stderr, "ERROR: No filename specified\n"); + return 2; + } + cmd_extract(argv[2]); + } else if (strcmp("boot", argv[1]) == 0) { + if (argc < 3) { + fprintf(stderr, "ERROR: No MZF file specified\n"); + return 2; + } + cmd_boot(argv[2]); + } else { + fprintf(stderr, "ERROR: Unknown command '%s'\n", argv[1]); + usage(); + return 1; + } + + return 0; +} diff --git a/projects/tzpuPico/tools/MZFD/MZFDTool.c.original b/projects/tzpuPico/tools/MZFD/MZFDTool.c.original new file mode 100755 index 0000000..8aa168f --- /dev/null +++ b/projects/tzpuPico/tools/MZFD/MZFDTool.c.original @@ -0,0 +1,630 @@ +#define VERSION "1.01" + +#include +#include +#include +#include +#include +#include + + + +union REGS regs; +struct SREGS sregs; + + +struct MZ700BootStruct +{ + unsigned char Type; + char Signature[6]; + char Name[11]; + unsigned short StartAddress; + unsigned short FileSize; + unsigned char Dummy[8]; + unsigned short SectorAddress; +}; + + +struct MZ700DirectoryStruct +{ + unsigned char FileType; // 0x00 -> 1 + char FileName[17]; // 0x01 ... 0x11 -> 17 + unsigned char LockFlag; // 0x12 -> 1 + unsigned char DummyFlag; // 0x13 -> 1 + unsigned short FileSize; // 0x14 ... 0x15 -> 2 + unsigned short LoadAddress; // 0x16 ... 0x17 -> 2 + unsigned short ExecAddress; // 0x18 ... 0x19 -> 2 + char Dummy[4]; // 0x1A ... 0x1D -> 4 + unsigned short SectorAddress; // 0x1E ... 0x1F -> 2 +}; + + +struct CMTHeader +{ + unsigned char Attribute; + char Name[17]; + unsigned short Size; + unsigned short LoadAddress; + unsigned short ExecAddress; + char Comment[104]; +}; + + + + +char FileTypes[][4] = { + "???", + "OBJ", + "BTX", + "BSD", + "BRD", + "RB ", + "???", + "LIB", + "???", + "???", + "SYS", + "GR " + }; + + + +unsigned char DiskParameters[11] = + {0xDF, 0x02, 0x25, 0x01, 16, 0x4E, 0xFF, 0x6C, 0xE5, 100, 8}; +// {0xDF, 0x02, 0x25, 0x02, 18, 0x1B, 0xFF, 0x6C, 0xF6, 100, 8}; /* 1.44MB */ + + +char *ErrorMsg[] = +{ + "success", + "invalid function", + "address mark not found", + "disk write-protected", + "sector not found / read error", + "reset faild", + "data did not verify correctly", + "disk changed", + "drive parameter activity failed", + "DMA overrun", + "data boundary error", + "bad sector detected", + "bad track detected", + "unsupported track or invalid media", + "invalid number of sectors", + "control data address mark detected", + "DMA arbitration level out of range", + "uncorrectable CRC or ECCerror", + "data ECC corrected" +}; + + +unsigned char SectorBuffer[1024]; + +unsigned char Drive; +unsigned char Cylinder; +unsigned char Head; +unsigned char Sector; + + +unsigned int OrgInt1EOffset; +unsigned int OrgInt1ESegment; + + + + +void Error(unsigned char Index) +{ + printf("Error: %02X - ", Index); + if(Index < 0x12) printf("%s\n", ErrorMsg[Index]); + if(Index == 0x80) puts("No disc in drive!"); + + exit(1); +} + + + + +void ShowSectorBuffer(void) +{ + unsigned char ByteCounter; + unsigned char LineCounter; + int Counter; + + + Counter = 0; + + for(LineCounter = 0; LineCounter < 16; LineCounter++) + { + printf("\n%04X : ", Counter); + for(ByteCounter = 0; ByteCounter < 16; ByteCounter++) + { + printf("%02X ", SectorBuffer[Counter + ByteCounter]); + } + printf(" "); + for(ByteCounter = 0; ByteCounter < 16; ByteCounter++) + { + printf("%c", iscntrl(SectorBuffer[Counter]) ? ' ' : SectorBuffer[Counter]); + Counter++; + } + } + puts(""); +} + + + + +int ResetFloppy(char Drive) +{ + regs.h.ah = 0; + regs.h.dl = Drive; + + int86(0x13, ®s, ®s); + + return(regs.h.ah); +} + + + + +int ReadSector(char Drive, char Cylinder, char Head, char Sector) +{ + int Counter; + + + if(Head == 0) Head = 1; + else Head = 0; + + + regs.h.ah = 0x02; + Counter = 3; + + while((regs.h.ah != 0) && (Counter != 0)) + { + regs.h.ah = 0x02; + regs.h.al = 1; + regs.h.ch = Cylinder; + regs.h.cl = Sector; + regs.h.dh = Head; + regs.h.dl = Drive; + + sregs.es = FP_SEG(SectorBuffer); + regs.x.bx = FP_OFF(SectorBuffer); + + int86x(0x13, ®s, ®s, &sregs); + Counter--; + } + + for(Counter = 0; Counter < sizeof(SectorBuffer); Counter++) + SectorBuffer[Counter] = ~SectorBuffer[Counter]; + + return(regs.x.ax); +} + + + +void SaveInt1E(void) +{ + OrgInt1EOffset = peek(0x0000, 0x1E * 4); + OrgInt1ESegment = peek(0x0000, 0x1E * 4 + 2); +} + + + + +void SetInt1E(unsigned char Type) +{ + Type++; + asm cli; + poke(0x0000, 0x1E * 4, FP_OFF(DiskParameters)); + poke(0x0000, 0x1E * 4 + 2, FP_SEG(DiskParameters)); + asm sti; +} + + + + +void ResetInt1E(void) +{ + asm cli; + poke(0x0000, 0x1E * 4, OrgInt1EOffset); + poke(0x0000, 0x1E * 4 + 2, OrgInt1ESegment); + asm sti; +} + + + + +void ShowDirectory(void) +{ + unsigned char SectorCounter; + unsigned char Counter; + unsigned char C, H, S; + struct MZ700BootStruct *Boot; + struct MZ700DirectoryStruct *Entry; + char FileName[18]; + unsigned int Result; +// unsigned short LastUsedSector; + + + puts(""); + + Result = ReadSector(Drive, 0, 0, 1); + Result = Result >> 8; + if(Result != 0) Error(Result); + Boot = (struct MZ700BootStruct *) SectorBuffer; + memcpy(FileName, Boot->Signature, 6); + FileName[6] = '\0'; + if((Boot->Type == 3) && (strcmp(FileName, "IPLPRO") == 0)) + { + puts("Floppy is bootable:"); + puts(""); + + memcpy(FileName, Boot->Name, 11); + if(strchr(FileName, 0x0D) != NULL) *strchr(FileName, 0x0D) = '\0'; + else FileName[11] = '\0'; + + printf(" %-17s OBJ %5u ", FileName, Boot->FileSize); + S = Boot->SectorAddress % 16 + 1; + H = (Boot->SectorAddress / 16) % 2; + C = Boot->SectorAddress / 16 / 2; + printf(" %4u (%2u %u %2u)\n", Boot->SectorAddress, C, H, S); + } + else + { + puts("Floppy is not bootable."); + } + + puts(""); + puts("Directory:"); + puts(""); + puts(" Name Type Size Lock Load Exec Pos ( C H S)"); + puts(""); + + for(SectorCounter = 1; SectorCounter < 17; SectorCounter++) + { + ReadSector(Drive, 0, 1, SectorCounter); + + + + for(Counter = 0; Counter < 256 / 32; Counter++) + { + Entry = (struct MZ700DirectoryStruct *) &SectorBuffer[Counter * sizeof(struct MZ700DirectoryStruct)]; + +// if((SectorCounter == 1) && (Counter == 0)) LastUsedSector = Entry->SectorAddress; + + if((Entry->FileType > 0) && (Entry->FileType < 0x80)) + { + memcpy(FileName, Entry->FileName, 17); + if(strchr(FileName, 0x0D) != NULL) *strchr(FileName, 0x0D) = '\0'; + else FileName[17] = '\0'; + + printf(" %-17s %s %5u %c %04X %04X", FileName, FileTypes[Entry->FileType], Entry->FileSize, Entry->LockFlag ? 'X' : ' ', Entry->LoadAddress, Entry->ExecAddress); + S = Entry->SectorAddress % 16 + 1; + H = (Entry->SectorAddress / 16) % 2; + C = Entry->SectorAddress / 16 / 2; + printf(" %4u (%2u %u %2u)\n", Entry->SectorAddress, C, H, S); + } + } + } +// printf("\nLast used Sector: %u\n\n", LastUsedSector); +} + + + + +void CopyFDToMZF(char *FileName) +{ + char MZFFileName[13]; + char EntryFileName[18]; + unsigned char SectorCounter, Counter; + FILE *MZFFile; + struct MZ700DirectoryStruct *Entry; + struct CMTHeader MZFHeader; + unsigned char C, H, S; + unsigned short WriteSize; + unsigned int Result; + + + S = 0; + + Result = ReadSector(Drive, 0, 0, 1); // BootSector + Result = Result >> 8; + if(Result != 0) Error(Result); + SectorBuffer[33] = '\0'; // emergency stop + if(strstr(SectorBuffer, FileName) != NULL) + { + Entry = (struct MZ700DirectoryStruct *) SectorBuffer; + S = Entry->SectorAddress % 16 + 1; + H = (Entry->SectorAddress / 16) % 2; + C = Entry->SectorAddress / 16 / 2; + Entry->FileType = 1; + } + else + { + for(SectorCounter = 1; (SectorCounter < 17) && (S == 0); SectorCounter++) + { + ReadSector(Drive, 0, 1, SectorCounter); + + for(Counter = 0; (Counter < 256 / 32) && (S == 0); Counter++) + { + Entry = (struct MZ700DirectoryStruct *) &SectorBuffer[Counter * sizeof(struct MZ700DirectoryStruct)]; + if((Entry->FileType > 0) && (Entry->FileType < 0x80)) + { + memcpy(EntryFileName, Entry->FileName, 17); + if(strchr(EntryFileName, 0x0D) != NULL) *strchr(EntryFileName, 0x0D) = '\0'; + else EntryFileName[17] = '\0'; + if(strcmp(FileName, EntryFileName) == 0) + { + S = Entry->SectorAddress % 16 + 1; + H = (Entry->SectorAddress / 16) % 2; + C = Entry->SectorAddress / 16 / 2; + } + } + } + } + } + + if(S != 0) + { + memset(MZFFileName, 0, sizeof(MZFFileName)); + strncpy(MZFFileName, FileName, 8); + strcat(MZFFileName, ".MZF"); + printf("%s found save it as %s\n", FileName, MZFFileName); + MZFFile = fopen(MZFFileName , "wb"); + memset(&MZFHeader, 0, sizeof(MZFHeader)); + if(Entry->FileType == 0x02) MZFHeader.Attribute = 0x05; + else MZFHeader.Attribute = Entry->FileType; + memcpy(MZFHeader.Name, Entry->FileName, 17); + MZFHeader.Size = Entry->FileSize; + strcat(MZFHeader.Comment, "by MZFDTool"); + + if((Entry->FileType == 0x01) && (Entry->LoadAddress == 0x0000)) + { + MZFHeader.Comment[24] = 0x21; + MZFHeader.Comment[25] = 0x30; + MZFHeader.Comment[26] = 0x11; + + MZFHeader.Comment[27] = 0x01; + MZFHeader.Comment[28] = 0x10; + MZFHeader.Comment[29] = 0x00; + + MZFHeader.Comment[30] = 0x11; + MZFHeader.Comment[31] = 0xF0; + MZFHeader.Comment[32] = 0xCF; + + MZFHeader.Comment[33] = 0xED; + MZFHeader.Comment[34] = 0xB0; + + MZFHeader.Comment[35] = 0xC3; + MZFHeader.Comment[36] = 0xF0; + MZFHeader.Comment[37] = 0xCF; + + + + MZFHeader.Comment[40] = 0x21; + MZFHeader.Comment[41] = 0x00; + MZFHeader.Comment[42] = 0x12; + + MZFHeader.Comment[43] = 0x01; + MZFHeader.Comment[44] = (unsigned char) Entry->FileSize & 0xFF; + MZFHeader.Comment[45] = Entry->FileSize >> 8; + + MZFHeader.Comment[46] = 0x11; + MZFHeader.Comment[47] = 0x00; + MZFHeader.Comment[48] = 0x00; + + MZFHeader.Comment[49] = 0xD3; + MZFHeader.Comment[50] = 0xE0; + + MZFHeader.Comment[51] = 0xED; + MZFHeader.Comment[52] = 0xB0; + + MZFHeader.Comment[53] = 0xC3; + MZFHeader.Comment[54] = 0x00; + MZFHeader.Comment[55] = 0x00; + + MZFHeader.LoadAddress = 0x1200; + MZFHeader.ExecAddress = 0x1120; + } + else + { + MZFHeader.LoadAddress = Entry->LoadAddress; + MZFHeader.ExecAddress = Entry->ExecAddress; + } + fwrite(&MZFHeader, sizeof(MZFHeader), 1, MZFFile); + + while(MZFHeader.Size != 0) + { + WriteSize = 256; + if(MZFHeader.Size < WriteSize) WriteSize = MZFHeader.Size; + MZFHeader.Size = MZFHeader.Size - WriteSize; + ReadSector(Drive, C, H, S); + fwrite(SectorBuffer, WriteSize, 1, MZFFile); + S++; + if(S == 17) + { + S = 1; + if(H == 0) H = 1; + else + { + C++; + H = 0; + } + } + } + + fclose(MZFFile); + } + else + { + puts(""); + printf("%s not found!\n", FileName); + puts(""); + puts("Remember that capital letters are differenced!"); + } +} + + + + +void CopyMZFToFD(char *FileName) +{ + printf("Not implemented yet (%s)\n", FileName); +} + + + + +void Copy(char *FileName) +{ + if((strstr(FileName, ".MZF") == NULL) && (strstr(FileName, ".mzf") == NULL)) CopyFDToMZF(FileName); + else CopyMZFToFD(FileName); +} + + + + +void ShowMap(void) +{ + unsigned short UsedSectors; + unsigned short Counter; + unsigned char BitCounter; +// unsigned char BitMask; + unsigned short BitMask; + unsigned short *SectorsPerTrack; + unsigned char StartTrack; + unsigned int Result; + + + puts(""); + + Result = ReadSector(Drive, 0, 0, 16); + Result = Result >> 8; + if(Result != 0) Error(Result); + UsedSectors = SectorBuffer[3] * 256 + SectorBuffer[4]; + StartTrack = SectorBuffer[5]; + + printf("Volume: %c%c%c Used Sectors: %4u\n\n", SectorBuffer[0], SectorBuffer[1], SectorBuffer[2], UsedSectors); + puts(""); + + puts(" 1111111"); + puts("Track ( C H S) 1234567890123456"); + puts(""); + + for(Counter = StartTrack; Counter <= 80; Counter++) + { + printf(" %3d (%2d %s 1) ", Counter, (Counter - 1) / 2, (Counter - 1) % 2 ? "1" : "0"); + BitMask = 0x0001; + SectorsPerTrack = (unsigned short *) &SectorBuffer[6 + 2 * (Counter - StartTrack)]; + + for(BitCounter = 0; BitCounter < 16; BitCounter++) + { + if(*SectorsPerTrack & BitMask) printf("X"); + else printf("-"); + BitMask = BitMask << 1; + } + puts(""); + } + + puts(""); +} + + + + +void Init(void) +{ + memset(SectorBuffer, 0, sizeof(SectorBuffer)); + Drive = 0; + Cylinder = 0; + Head = 0; + Sector = 1; +} + + + + + + + + +void Usage(void) +{ + puts("usage: mzfdtool dir [DRIVE:]"); + puts(" map [DRIVE:]"); + puts(" copy [DRIVE:] FILENAME"); + puts(" DRIVE: C H S"); + puts(""); + exit(1); +} + + + + +int main(int argc, char *argv[]) +{ + unsigned int Result; + + + printf("\nMZFDTool V %s (c) 2002 by BKK\n\n", VERSION); + + if(argc == 1) Usage(); + + Init(); + + SetInt1E(0); + + ResetFloppy(Drive); + + if(strcmp(argv[1], "dir") == 0) + { + if(argc == 3) + if(toupper(argv[2][0]) == 'B') Drive = 1; + ShowDirectory(); + } + else + { + if(strcmp(argv[1], "copy") == 0) + { + if(argc == 4) + { + if(toupper(argv[2][0]) == 'B') Drive = 1; + Copy(argv[3]); + } + else Copy(argv[2]); + } + else + { + if(strcmp(argv[1], "map") == 0) + { + if(argc == 3) + if(toupper(argv[2][0]) == 'B') Drive = 1; + ShowMap(); + } + else + { + if(argc < 3) exit(1); + + if(toupper(argv[1][0]) == 'B') Drive = 1; + Cylinder = atoi(argv[2]); + Head = atoi(argv[3]); + Sector = atoi(argv[4]); + + printf("Drive: %c CHS: %u %u %u\n", Drive ? 'B' : 'A', Cylinder, Head, Sector); + +// Set_Floppy_Medis_Type(); + + Result = ReadSector(Drive, Cylinder, Head, Sector); + Result = Result >> 8; + + if(Result == 0) ShowSectorBuffer(); + else Error(Result); + } + } + } + + ResetInt1E(); + ResetFloppy(Drive); + + return(0); +} \ No newline at end of file diff --git a/projects/tzpuPico/tools/MZFD/Makefile b/projects/tzpuPico/tools/MZFD/Makefile new file mode 100644 index 0000000..82f4dd5 --- /dev/null +++ b/projects/tzpuPico/tools/MZFD/Makefile @@ -0,0 +1,19 @@ +# MZFDTool - Sharp MZ-700 Floppy Disk image tool +# (c) 2002 BKK, 2026 Philip Smart +# +# Usage: +# make Build MZFDTool +# make clean Remove build artifacts + +CC = gcc +CFLAGS = -Wall -Wno-unused-result -O2 +TARGET = MZFDTool +SRC = MZFDTool.c + +$(TARGET): $(SRC) + $(CC) $(CFLAGS) -o $@ $< + +clean: + rm -f $(TARGET) + +.PHONY: clean