Created
January 12, 2025 19:47
-
-
Save AkBKukU/a1a4d5b23ec650affbbd4fbc9c719b26 to your computer and use it in GitHub Desktop.
Xebec Viewer By zer0xoms a tool for viewing Xebec MFM HDD transition data
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// By zer0xoms a tool for viewing Xebec MFM HDD transition data | |
// Written on Windows, sorry. :D | |
#include "stdlib.h" | |
#include "string.h" | |
#include "stdio.h" | |
#include "math.h" | |
//#define POLY 0x8408 /* 1021H bit reversed */ | |
FILE *fhin, *fhout; | |
char *infile, *outfile; | |
char *head_view; | |
char *buffer, *bitstream; | |
char filename[1024]; | |
unsigned char header[16]; | |
unsigned char sector[1024]; | |
unsigned char sectorlog[306 * 4 * 17]; | |
unsigned char disk[306 * 4 * 17 * 512]; | |
unsigned char trk_image[2048 * 2048 * 3]; | |
unsigned int vis_mult = 1; | |
unsigned int vis_width = 2048*vis_mult; | |
unsigned int vis_height = 2048*vis_mult; | |
unsigned int vis_size = vis_width * vis_height; | |
float vis_xcenter = (float) vis_width/2; | |
float vis_ycenter = (float) vis_height/2; | |
unsigned int trk_dist = 320*vis_mult; | |
unsigned int trk_iwidth = 2*vis_mult; // track width | |
float trk_width = (float) trk_iwidth; | |
int tTrack; | |
// truecolor bmp header | |
unsigned char trk_img_hdr[] = {0x42, 0x4D, vis_size*3+0x38, (vis_size*3+0x38)>>8, (vis_size*3+0x38)>>16, (vis_size*3+0x38)>>24, 0x00, 0x00, 0x00, 0x00, 0x36, 0x00, 0x00, 0x00, 0x28, 0x00, | |
0x00, 0x00, vis_width, vis_width>>8, 0x00, 0x00, vis_height, vis_height>>8, 0x00, 0x00, 0x01, 0x00, 0x18, 0x00, 0x00, 0x00, | |
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12, 0x0B, 0x00, 0x00, 0x12, 0x0B, 0x00, 0x00, 0x00, 0x00, | |
0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; | |
int ropen() | |
{ | |
fhin=fopen(infile,"rb"); | |
if (!fhin) | |
{ | |
// printf("\nERROR: Couldn't open file %s!\n", infile); | |
// exit(-1); | |
return -1; | |
} | |
return 0; | |
} | |
void rclose() | |
{ | |
fclose(fhin); | |
} | |
int wopen() | |
{ | |
fhout=fopen(outfile,"wb"); | |
if (!fhout) | |
{ | |
printf("\nERROR: Couldn't open file %s!\n", outfile); | |
// exit(-1); | |
return -1; | |
} | |
return 0; | |
} | |
void wclose() | |
{ | |
fclose(fhout); | |
} | |
// --------------------------------------------------------------------------------------------------------------------------------------------------------------- | |
/* | |
unsigned short crc16(unsigned char *data_p, unsigned short length) | |
{ | |
unsigned char i; | |
unsigned int data; | |
unsigned int crc = 0xffff; | |
if (length == 0) | |
return (~crc); | |
do | |
{ | |
for (i=0, data=(unsigned int)0xff & *data_p++;i < 8;i++, data <<= 1) | |
{ | |
if ((crc & 0x0001) ^ ((data >> 7) & 0x0001)) crc = (crc >> 1) ^ POLY; | |
else crc >>= 1; | |
} | |
} while (--length); | |
// crc = ~crc; | |
data = crc; | |
for (i=0;i<16;i++) | |
{ | |
crc = crc << 1; | |
crc = crc | ((data >> i) & 0x0001); | |
} | |
// crc = (crc << 8) | (data >> 8 & 0xff); | |
return (crc); | |
} | |
unsigned long int crc_byte( unsigned long input, unsigned long divisor ) | |
{ | |
int k; | |
for (k = 8; k; k--) | |
{ | |
input = input & 1 ? (input >> 1) ^ divisor : input >> 1; | |
} | |
return input; | |
} | |
*/ | |
unsigned int crc32(unsigned int initval, unsigned int poly, unsigned char *data, unsigned int len) | |
{ | |
unsigned int crc = initval; | |
unsigned int i = len; | |
unsigned char *d = data; | |
while (i-- > 0) { | |
unsigned char x = *(d++); | |
for (int j=0; j<8; j++) { | |
unsigned char feedback = (crc >> 31) ^ (x >> 7); | |
crc = (crc << 1); | |
if (feedback) | |
crc ^= poly; | |
x = x << 1; | |
} | |
} | |
return crc; | |
/* | |
for (i = 0; i < len; i++) | |
{ | |
crc = crc_byte( ( crc ^ data[ i ] ) & 0xff, 0xEDB88320 ) ^ ( crc >> 8 ); | |
} | |
return crc ^ 0xFFFFFFFF;*/ | |
} | |
// --------------------------------------------------------------------------------------------------------------------------------------------------------------- | |
int readMFMword(unsigned int offset) | |
{ | |
unsigned int temp1, temp1a, temp1b, temp2; | |
temp1a = offset >> 3; | |
temp1b = offset - (temp1a << 3); | |
if (temp1b == 0) | |
{ | |
temp1 = buffer[temp1a] & 0xff; | |
temp2 = buffer[temp1a+1] & 0xff; | |
} | |
else | |
{ | |
temp1 = ((buffer[temp1a] & 0xff) << temp1b) | ((buffer[temp1a+1] & 0xff) >> (8-temp1b)); | |
temp2 = ((buffer[temp1a+1] & 0xff) << temp1b) | ((buffer[temp1a+2] & 0xff) >> (8-temp1b)); | |
} | |
return (((temp1 << 8) & 0xff00) | (temp2 & 0xff)); | |
} | |
int decodeMFMword(unsigned int offset_1) // Byte 1 = _7_6_5_4 / Byte 2 = _3_2_1_0 | |
{ | |
unsigned int temp1, temp1a, temp1b, temp2; | |
temp1a = offset_1 >> 3; | |
temp1b = offset_1 - (temp1a << 3); | |
if (temp1b == 0) | |
{ | |
temp1 = (buffer[temp1a] & 0x55) << 1; | |
temp1 = (temp1 & 0x80) | ((temp1 & 0x20) << 1) | ((temp1 & 0x08) << 2) | ((temp1 & 0x02) << 3) & 0xf0; | |
temp2 = (buffer[temp1a+1] & 0x55); | |
temp2 = ((temp2 & 0x40) >> 3) | ((temp2 & 0x10) >> 2) | ((temp2 & 0x04) >> 1) | (temp2 & 0x01) & 0x0f; | |
} | |
else | |
{ | |
temp1 = ((((buffer[temp1a] & 0xff) << temp1b) | ((buffer[temp1a+1] & 0xff) >> (8-temp1b))) & 0x55) << 1; | |
temp1 = (temp1 & 0x80) | ((temp1 & 0x20) << 1) | ((temp1 & 0x08) << 2) | ((temp1 & 0x02) << 3) & 0xf0; | |
temp2 = ((((buffer[temp1a+1] & 0xff) << temp1b) | ((buffer[temp1a+2] & 0xff) >> (8-temp1b))) & 0x55); | |
temp2 = ((temp2 & 0x40) >> 3) | ((temp2 & 0x10) >> 2) | ((temp2 & 0x04) >> 1) | (temp2 & 0x01) & 0x0f; | |
} | |
return (temp1 | temp2); | |
} | |
// --------------------------------------------------------------------------------------------------------------------------------------------------------------- | |
void visualize(unsigned int drawStart, unsigned int drawEnd, unsigned int drawCol) // This turns bytes into circles | |
{ | |
float fS, fC, fdraw, fdrawlen, fdrawpos; | |
unsigned int draw, drawColor, drawCount; | |
int Xpos, Ypos; | |
unsigned char oldtemp; | |
for(drawCount = 0; drawCount < (drawEnd - drawStart); drawCount++) | |
{ | |
fdrawpos = (float) drawCount + drawStart; | |
fdrawlen = (float) 166496; // Hardcoded track length... | |
fdraw = (float) (360 / fdrawlen * fdrawpos * 0.017453292); | |
if (drawCol == 1) | |
{ | |
for(draw = 0; draw < trk_iwidth; draw++) | |
{ | |
fS = (float) (((306 - tTrack) * trk_width + trk_dist + draw) * cos(fdraw) + vis_ycenter); // 306 cylinders | |
fC = (float) (((306 - tTrack) * trk_width + trk_dist + draw) * sin(fdraw) + vis_xcenter); | |
Xpos = (int) (vis_width - fC); | |
Ypos = (int) (vis_height - fS); | |
if ((Xpos >= 0) && (Xpos < vis_width) && (Ypos >= 0) && (Ypos < vis_height)) | |
{ | |
trk_image[Xpos + Ypos * vis_width] = 0x00; | |
} | |
} | |
} | |
else | |
{ | |
for(draw = 0; draw < trk_iwidth; draw++) | |
{ | |
fS = (float) (((306 - tTrack) * trk_width + trk_dist + draw) * cos(fdraw) + vis_ycenter); // 306 cylinders | |
fC = (float) (((306 - tTrack) * trk_width + trk_dist + draw) * sin(fdraw) + vis_xcenter); | |
Xpos = (int) (vis_width - fC); | |
Ypos = (int) (vis_height - fS); | |
if ((Xpos >= 0) && (Xpos < vis_width) && (Ypos >= 0) && (Ypos < vis_height)) // && (trk_image[Xpos+Ypos*vis_width] == 128)) | |
{ | |
drawColor = sector[drawCount / 16]; | |
// trk_image[Xpos + Ypos * vis_width] = drawColor; | |
trk_image[(Xpos * 3) + 2 + Ypos * vis_width * 3] = 0; | |
trk_image[(Xpos * 3) + 1 + Ypos * vis_width * 3] = drawColor; | |
trk_image[(Xpos * 3) + Ypos * vis_width * 3] = 0; | |
} | |
} | |
} | |
} | |
} | |
// --------------------------------------------------------------------------------------------------------------------------------------------------------------- | |
int main(int argc, char* argv[]) | |
{ | |
unsigned int offset, offset2, size, check, bits, bitcount, count; | |
unsigned char byte; | |
infile = argv[1]; // Start from the transitions_file | |
head_view = argv[2]; // Start from the transitions_file | |
ropen(); | |
fseek(fhin, 0, SEEK_END); | |
size = ftell(fhin); | |
if (size == 0) | |
{ | |
rclose(); | |
return -1; | |
} | |
buffer = (char *) malloc(size); // No dangers here... | |
fseek(fhin, 0, SEEK_SET); | |
fread(buffer, 1, size, fhin); | |
rclose(); | |
memset(sectorlog, 0, sizeof(sectorlog)); | |
// goto dobitstream; // Use this to run faster | |
printf("Converting transition data to bitstream...\n"); | |
// Lets see how much space we need... | |
offset = 0; | |
bits = 0; | |
next: | |
if (buffer[offset] < 0x32) | |
{ | |
bits += 2; | |
} | |
else if (buffer[offset] < 0x44) | |
{ | |
bits += 3; | |
} | |
else | |
{ | |
bits += 4; | |
} | |
offset++; | |
if (offset < size) goto next; | |
// printf("%d bytes needed\n", bits >> 3); | |
bitstream = (char *) malloc(bits >> 3); // No need to check, modern computers have plenty. | |
// Now we transforms quickly and lazily the HDD flux timings into bitstream using hardcoded thresholds... | |
offset = 0; | |
offset2 = 0; | |
bitcount = 0; | |
more: | |
if (buffer[offset] < 0x32) // 10 | |
{ | |
byte = (byte << 1) | 1; | |
bitcount++; | |
if (bitcount == 8) | |
{ | |
bitstream[offset2++] = byte; | |
bitcount = 0; | |
} | |
byte = (byte << 1) | 0; | |
bitcount++; | |
if (bitcount == 8) | |
{ | |
bitstream[offset2++] = byte; | |
bitcount = 0; | |
} | |
} | |
else if (buffer[offset] < 0x44) // 100 | |
{ | |
byte = (byte << 1) | 1; | |
bitcount++; | |
if (bitcount == 8) | |
{ | |
bitstream[offset2++] = byte; | |
bitcount = 0; | |
} | |
byte = (byte << 1) | 0; | |
bitcount++; | |
if (bitcount == 8) | |
{ | |
bitstream[offset2++] = byte; | |
bitcount = 0; | |
} | |
byte = (byte << 1) | 0; | |
bitcount++; | |
if (bitcount == 8) | |
{ | |
bitstream[offset2++] = byte; | |
bitcount = 0; | |
} | |
} | |
else // 1000 | |
{ | |
byte = (byte << 1) | 1; | |
bitcount++; | |
if (bitcount == 8) | |
{ | |
bitstream[offset2++] = byte; | |
bitcount = 0; | |
} | |
byte = (byte << 1) | 0; | |
bitcount++; | |
if (bitcount == 8) | |
{ | |
bitstream[offset2++] = byte; | |
bitcount = 0; | |
} | |
byte = (byte << 1) | 0; | |
bitcount++; | |
if (bitcount == 8) | |
{ | |
bitstream[offset2++] = byte; | |
bitcount = 0; | |
} | |
byte = (byte << 1) | 0; | |
bitcount++; | |
if (bitcount == 8) | |
{ | |
bitstream[offset2++] = byte; | |
bitcount = 0; | |
} | |
} | |
offset++; | |
if (offset < size) goto more; | |
// Store the bitstream file. The previous part can be skipped by loading the bitstream directly. | |
outfile = "ST-412.bit"; | |
wopen(); | |
fwrite(bitstream, 1, offset2, fhout); | |
wclose(); | |
memcpy(buffer, bitstream, offset2); | |
size = offset2; | |
printf("Converting bitstream to disk imagefile...\n"); | |
// The following is written for ST-412 used on Xebec controller in mind | |
dobitstream: | |
offset = 0; | |
unsigned char MFMT = 0, MFMerrors = 0; | |
unsigned int MFMTrack, MFMHead, MFMSector; | |
unsigned int MFMSize, MFMHeaderOffset = 0; | |
do{ | |
// Lets try to find a sector header. | |
if ((readMFMword(offset) == 0xAAAA) && (readMFMword(offset + 16) == 0xAAAA) && (readMFMword(offset + 32) == 0xAAAA) && (readMFMword(offset + 48) == 0xAAAA) && (readMFMword(offset + 64) == 0xAAAA) && (readMFMword(offset + 80) == 0xAAAA) && (readMFMword(offset + 96) == 0xAAAA) && (readMFMword(offset + 112) == 0xAAAA) && (readMFMword(offset + 128) == 0xAAAA) && (readMFMword(offset + 144) == 0xAAA9) && (readMFMword(offset + 160) == 0x2AAA) && (readMFMword(offset + 176) == 0xAAAA) && (decodeMFMword(offset + 192) == 0xC2)) | |
{ | |
header[0] = decodeMFMword(offset + 160); // SYNC 00 | |
header[1] = decodeMFMword(offset + 176); // SYNC 00 | |
header[2] = decodeMFMword(offset + 192); // ADRM C2 | |
header[3] = decodeMFMword(offset + 208); // TRHI | |
header[4] = decodeMFMword(offset + 224); // TRLO | |
header[5] = decodeMFMword(offset + 240); // HEAD | |
header[6] = decodeMFMword(offset + 256); // SECT | |
header[7] = decodeMFMword(offset + 272); // FLAG | |
header[8] = decodeMFMword(offset + 288); // NULL | |
header[9] = decodeMFMword(offset + 304); // CRC1 | |
header[10] = decodeMFMword(offset + 320); // CRC2 | |
header[11] = decodeMFMword(offset + 336); // CRC3 | |
header[12] = decodeMFMword(offset + 352); // CRC4 | |
tTrack = MFMTrack = (header[3] << 8) | header[4]; | |
MFMHead = header[5]; | |
if ((MFMTrack < 0x1E) || ((MFMTrack == 0x1E) && (MFMHead < 0x03))) // Dirty hack to work around the skew shift at this particular position | |
{ | |
MFMSector = header[6] + 11; | |
if (MFMSector > 0x10) MFMSector -= 0x11; | |
} | |
else | |
{ | |
MFMSector = header[6] + 14; | |
if (MFMSector > 0x10) MFMSector -= 0x11; | |
} | |
MFMSize = 512; | |
if (crc32(0x00000000, 0x00A00805, header, 9) != ((header[9] << 24) | (header[10] << 16) | (header[11] << 8) | header[12])) // Calculate header checksum | |
{ | |
// for (count = 0; count < 13; count++) // Print header on bad header | |
// { | |
// printf("%02X ", header[count]); | |
// } | |
// printf("*\n"); | |
} | |
else if ((MFMTrack < 306) && (MFMHead < 4) && (MFMSector < 17)) // Cylinders, heads & sectors hardcoded here | |
{ | |
// printf("%08X\n", offset >> 3); // Print header on good header | |
// for (count = 0; count < 13; count++) | |
// { | |
// printf("%02X ", header[count]); | |
// } | |
// printf("\n"); | |
if (MFMHead == atoi(head_view)) // Plot just single head | |
{ | |
memcpy(sector, header, 13); | |
visualize(MFMSector * 608 * 16 + 53 * 16, MFMSector * 608 * 16 + 53 * 16 + 13 * 16, 0); // Draw sector header content into bitmap image | |
} | |
MFMT = 1; | |
MFMHeaderOffset = offset; | |
} | |
else | |
{ | |
// for (count = 0; count < 13; count++) // Print header on invalid header | |
// { | |
// printf("%02X ", header[count]); | |
// } | |
// printf("*BAD*\n"); | |
MFMT = 0; | |
} | |
} | |
// And then attempt with the actual sector data. | |
else if ((readMFMword(offset) == 0xAAAA) && (readMFMword(offset + 16) == 0xAAAA) && (readMFMword(offset + 32) == 0xAAAA) && (readMFMword(offset + 48) == 0xAAAA) && (readMFMword(offset + 64) == 0xAAAA) && (readMFMword(offset + 80) == 0xAAAA) && (readMFMword(offset + 96) == 0xAAAA) && (readMFMword(offset + 112) == 0xAAAA) && (readMFMword(offset + 128) == 0xAAAA) && (readMFMword(offset + 144) == 0xAAAA) && (readMFMword(offset + 160) == 0xAAAA) && (readMFMword(offset + 176) == 0xAAAA) && (readMFMword(offset + 192) == 0xAAAA) && (readMFMword(offset + 208) == 0xAAA9) && (readMFMword(offset + 224) == 0x2AAA) && (readMFMword(offset + 240) == 0xAAAA) && MFMT == 1) | |
{ | |
// for (count = 0; count < 13; count++) // Print some sector data | |
// { | |
// printf("%02X ", decodeMFMword(offset + 224 + count * 16)); | |
// } | |
// printf("<>\n"); | |
if ((offset - MFMHeaderOffset) > 800) // Skip sector if it's too far from the header | |
{ | |
// printf("MISSING HEADER\n"); | |
goto MFMfar; | |
} | |
for (count = 0; count < MFMSize + 6; count++) | |
{ | |
sector[count] = decodeMFMword(offset + 224 + count * 16); | |
} | |
if (crc32(0x00000000, 0x00A00805, sector + 2, 512) == ((sector[514] << 24) | (sector[515] << 16) | (sector[516] << 8) | sector[517])) // Calculate sector checksum | |
{ | |
for (count = 0; count < MFMSize; count++) // Copy sector to final disk imagefile (memcpy?) | |
{ | |
disk[MFMTrack * MFMSize * 17 * 4 + MFMHead * MFMSize * 17 + MFMSector * MFMSize + count] = sector[2 + count]; | |
} | |
sectorlog[MFMTrack * 17 * 4 + MFMHead * 17 + MFMSector] = 0x21; // Mark sector as found on the log | |
if (MFMHead == atoi(head_view)) // Plot just single head | |
{ | |
visualize(MFMSector * 608 * 16 + 85 * 16, MFMSector * 608 * 16 + 85 * 16 + ((MFMSize + 6) * 16), 0); // Draw sector data content to head bitmap image | |
} | |
} | |
else | |
{ | |
printf("%3d.%d BAD SECTOR %d\n", MFMTrack, MFMHead, MFMSector); | |
} | |
MFMfar: | |
MFMT = 0; | |
MFMHeaderOffset = 0; | |
} | |
offset++; | |
} while (offset < size * 8); | |
sprintf(filename, "%s_h%d.img",infile,atoi(head_view)); | |
//outfile = "ST-412.img"; // Output final disk imagefile | |
outfile = filename; | |
wopen(); | |
fwrite(disk, 1, sizeof(disk), fhout); | |
wclose(); | |
sprintf(filename, "%s_h%d.log",infile,atoi(head_view)); | |
//outfile = "ST-412.log"; // Output sector usage log (missing and/or bad) | |
outfile = filename; | |
wopen(); | |
fwrite(sectorlog, 1, sizeof(sectorlog), fhout); | |
wclose(); | |
sprintf(filename, "%s_h%d.bmp",infile,atoi(head_view)); | |
//outfile = "ST-412.bmp"; // Sector plot bmp, 8 bit with bad color palette. | |
outfile = filename; | |
wopen(); | |
fwrite(trk_img_hdr, 1, 0x36, fhout); // Full color version | |
fwrite(trk_image, 1, vis_size * 3, fhout); | |
fwrite(trk_image, 1, 2, fhout); | |
wclose(); | |
free(bitstream); // Do we need to free the bits anymore on modern compilers? | |
free(buffer); | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment