1 Commits

Author SHA1 Message Date
Philip Smart
fb496c9309 Added MZ Floppy Disk creation tool 2026-06-04 17:58:52 +01:00
4 changed files with 1267 additions and 0 deletions

BIN
projects/tzpuPico/tools/MZFD/MZFDTool vendored Executable file

Binary file not shown.

View File

@@ -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 <file.mzf> [-o disk.img] Add MZF file to directory
* MZFDTool extract <name> [-o disk.img] Extract file to MZF
* MZFDTool boot <file.mzf> [-o disk.img] Set boot program
* ========================================================================= */
#define VERSION "2.00"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <ctype.h>
#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 <file.mzf> [-o disk.img] Add MZF file to disk\n");
printf(" MZFDTool extract <name> [-o disk.img] Extract file to MZF\n");
printf(" MZFDTool boot <file.mzf> [-o disk.img] Set boot program\n");
printf("\n");
printf("Options:\n");
printf(" -o <filename> 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;
}

View File

@@ -0,0 +1,630 @@
#define VERSION "1.01"
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>
#include <mem.h>
#include <dos.h>
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, &regs, &regs);
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, &regs, &regs, &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);
}

19
projects/tzpuPico/tools/MZFD/Makefile vendored Normal file
View File

@@ -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