svicpm2dmk.cc 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239
  1. #include <string>
  2. #include <stdexcept>
  3. #include <vector>
  4. #include <cassert>
  5. #include <cstdio>
  6. #include <cstring>
  7. #include <cstdlib>
  8. #include <sys/stat.h>
  9. #include <sys/types.h>
  10. using namespace std;
  11. typedef unsigned char byte; // 8 bit
  12. typedef unsigned short word; // 16 bit
  13. static const unsigned int TRACK_LENGTH = 6250;
  14. struct DiskInfo
  15. {
  16. int gap1;
  17. int gap2;
  18. int gap3;
  19. int gap4a;
  20. int sectorsPerTrack;
  21. int numberCylinders;
  22. int sectorSizeCode;
  23. };
  24. struct DmkHeader
  25. {
  26. byte writeProtected;
  27. byte numTracks;
  28. byte trackLen[2];
  29. byte flags;
  30. byte reserved[7];
  31. byte format[4];
  32. };
  33. class File
  34. {
  35. public:
  36. File(const string& filename, const char* mode)
  37. : f(fopen(filename.c_str(), mode))
  38. {
  39. if (!f) {
  40. throw runtime_error("Couldn't open: " + filename);
  41. }
  42. }
  43. ~File()
  44. {
  45. fclose(f);
  46. }
  47. void read(void* data, int size)
  48. {
  49. if (fread(data, size, 1, f) != 1) {
  50. throw runtime_error("Couldn't read file");
  51. }
  52. }
  53. void write(const void* data, int size)
  54. {
  55. if (fwrite(data, size, 1, f) != 1) {
  56. throw runtime_error("Couldn't write file");
  57. }
  58. }
  59. private:
  60. FILE* f;
  61. };
  62. static void updateCrc(word& crc, byte val)
  63. {
  64. for (int i = 8; i < 16; ++i) {
  65. crc = (crc << 1) ^ ((((crc ^ (val << i)) & 0x8000) ? 0x1021 : 0));
  66. }
  67. }
  68. static void fill(byte*& p, int len, byte value)
  69. {
  70. memset(p, value, len);
  71. p += len;
  72. }
  73. void convert(const DiskInfo& info, const string& input, const string& output)
  74. {
  75. // Single or double sided input image?
  76. struct stat st;
  77. stat(input.c_str(), &st);
  78. int numSides;
  79. switch (st.st_size) {
  80. case 174080:
  81. numSides = 1;
  82. break;
  83. case 348160:
  84. numSides = 2;
  85. break;
  86. default:
  87. throw runtime_error(
  88. "input filesize should be exactly 174080 or 348160 bytes.\n");
  89. }
  90. int sectorSize = 128 << info.sectorSizeCode;
  91. int totalTracks = numSides * info.numberCylinders;
  92. int totalSectors = totalTracks * info.sectorsPerTrack;
  93. int totalSize = totalSectors * sectorSize;
  94. // sanity check
  95. if (st.st_size != totalSize) {
  96. throw runtime_error("Wrong input filesize");
  97. }
  98. File inf(input, "rb");
  99. File outf(output, "wb");
  100. int rawSectorLen =
  101. 12 + 10 + info.gap2 + 12 + 4 +
  102. sectorSize + 2 + info.gap3;
  103. int gap4b = TRACK_LENGTH - (info.gap4a + 12 + 4 + info.gap1 +
  104. info.sectorsPerTrack * rawSectorLen);
  105. assert(gap4b > 0);
  106. int dmkTrackLen = TRACK_LENGTH + 128;
  107. DmkHeader header;
  108. memset(&header, 0, sizeof(header));
  109. header.numTracks = info.numberCylinders;
  110. header.trackLen[0] = dmkTrackLen & 0xff;
  111. header.trackLen[1] = dmkTrackLen >> 8;
  112. header.flags = ((numSides == 2) ? (0 << 4) : (1 << 4)) |
  113. (0 << 6); // double density (MFM)
  114. outf.write(&header, sizeof(header));
  115. vector<byte*> addrPos(info.sectorsPerTrack);
  116. vector<byte*> dataPos(info.sectorsPerTrack);
  117. vector<byte> buf(dmkTrackLen); // zero-initialized
  118. byte* ip = &buf[ 0]; // pointer in IDAM table
  119. byte* tp = &buf[128]; // pointer in actual track data
  120. fill(tp, info.gap4a, 0x4e); // gap4a
  121. fill(tp, 12, 0x00); // sync
  122. fill(tp, 3, 0xc2); // index mark
  123. fill(tp, 1, 0xfc); //
  124. fill(tp, info.gap1, 0x4e); // gap1
  125. for (int sec = 0; sec < info.sectorsPerTrack; ++sec) {
  126. fill(tp, 12, 0x00); // sync
  127. fill(tp, 3, 0xa1); // ID addr mark
  128. int pos = tp - &buf[0];
  129. assert(pos < 0x4000);
  130. *ip++ = pos & 0xff;
  131. *ip++ = (pos >> 8) | 0x80; // double density (MFM) sector
  132. fill(tp, 1, 0xfe); // ID addr mark (cont)
  133. addrPos[sec] = tp;
  134. fill(tp, 6, 0x00); // C H R N CRC (overwritten later)
  135. fill(tp, info.gap2, 0x4e); // gap2
  136. fill(tp, 12, 0x00); // sync
  137. fill(tp, 3, 0xa1); // data mark
  138. fill(tp, 1, 0xfb); //
  139. dataPos[sec] = tp;
  140. fill(tp, sectorSize, 0x00); // sector data (overwritten later)
  141. fill(tp, 2, 0x00); // CRC (overwritten later)
  142. fill(tp, info.gap3, 0x4e); // gap3
  143. }
  144. fill(tp, gap4b, 0x4e); // gap4b
  145. assert((tp - &buf[0]) == dmkTrackLen);
  146. for (int cyl = 0; cyl < info.numberCylinders; ++cyl) {
  147. for (int head = 0; head < numSides; ++head) {
  148. for (int sec = 0; sec < info.sectorsPerTrack; ++sec) {
  149. byte* ap = addrPos[sec];
  150. *ap++ = cyl;
  151. *ap++ = head;
  152. *ap++ = sec + 1;
  153. *ap++ = info.sectorSizeCode;
  154. word addrCrc = 0xffff;
  155. const byte* t1 = ap - 8;
  156. for (int i = 0; i < 8; ++i) {
  157. updateCrc(addrCrc, t1[i]);
  158. }
  159. *ap++ = addrCrc >> 8;
  160. *ap++ = addrCrc & 0xff;
  161. byte* dp = dataPos[sec];
  162. inf.read(dp, sectorSize);
  163. dp += sectorSize;
  164. word dataCrc = 0xffff;
  165. const byte* t2 = dp - sectorSize - 4;
  166. for (int i = 0; i < sectorSize + 4; ++i) {
  167. updateCrc(dataCrc, t2[i]);
  168. }
  169. *dp++ = dataCrc >> 8;
  170. *dp++ = dataCrc & 0xff;
  171. }
  172. outf.write(&buf[0], dmkTrackLen);
  173. }
  174. }
  175. }
  176. int main(int argc, char** argv)
  177. {
  178. if (argc != 3) {
  179. printf("svicpm2dmk\n"
  180. "---------\n"
  181. "\n"
  182. "Utility to convert a SVI CP/M disk image into a DMK disk image.\n"
  183. "\n"
  184. "The SVI CP/M image is expected to have to following format:\n"
  185. " - All tracks contain 17 sectors of 256 bytes.\n"
  186. " - There are 40 tracks on the disk.\n"
  187. " - The disk can be either single or double sided.\n"
  188. "This means that the size of the input image should be exactly\n"
  189. "174080 bytes for single sided disks or 348160 bytes for double\n"
  190. "sided disks.\n"
  191. "\n"
  192. "Usage: %s <input.dsk> <output.dmk>\n", argv[0]);
  193. exit(1);
  194. }
  195. // TODO add command line options to make these parameters configurable
  196. DiskInfo info;
  197. info.gap1 = 50;
  198. info.gap2 = 22;
  199. info.gap3 = 34;
  200. info.gap4a = 80;
  201. info.sectorsPerTrack = 17;
  202. info.numberCylinders = 40;
  203. info.sectorSizeCode = 1; // 256 = 128 << 1
  204. try {
  205. convert(info, argv[1], argv[2]);
  206. } catch (std::exception& e) {
  207. fprintf(stderr, "Error: %s\n", e.what());
  208. }
  209. }