Skip to content

Instantly share code, notes, and snippets.

@AkBKukU
Created January 12, 2025 19:47
Show Gist options
  • Save AkBKukU/a1a4d5b23ec650affbbd4fbc9c719b26 to your computer and use it in GitHub Desktop.
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
// 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