more scanline shader parameters
[zxemut.git] / wd93.d
blobfefcfd8f67655a3576fdf18ed9dcd195c141298c
1 /* ZX Spectrum Emulator
2 * Copyright (C) 2012-2017 Ketmar // Invisible Vector
3 * This file originating from Unreal Spectrum (DeathSoft mod)
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 module wd93;
19 private:
21 import iv.bclamp;
22 import iv.cmdcon;
23 import iv.vfs;
24 import iv.zymosis;
26 import emuconfig;
29 // ////////////////////////////////////////////////////////////////////////// //
30 public __gshared ubyte[] wdsnapbuf; // large temporary buffer (for reading snapshots)
31 public __gshared uint wdsnapsize;
33 shared static this () { wdsnapbuf.length = 8*1048576; }
35 public __gshared ubyte[] defaultBootHob;
37 //@property int z80tstates () nothrow @trusted @nogc { return emuz80.tstates; }
38 @property long totalTS () nothrow @trusted @nogc { return emuFullFrameTS+cast(long)emuz80.tstates; }
40 @property ushort z80pc () nothrow @trusted @nogc { return emuz80.PC; }
41 @property void z80pc (uint v) nothrow @trusted @nogc { emuz80.PC = v&0xffff; }
43 @property ushort z80pop () nothrow @trusted { return emuz80.pop(); }
45 public __gshared ubyte delegate (uint pc) nothrow trdosROMread;
48 // ////////////////////////////////////////////////////////////////////////// //
49 __gshared bool trdTrapsDebug = false;
51 shared static this () {
52 conRegVar!trdTrapsDebug("trd_debug_traps", "show TRD traps debug info");
56 // ////////////////////////////////////////////////////////////////////////// //
57 shared int led1793Load;
58 shared int led1793Save;
59 shared int led1793Format;
60 shared int led1793Seek;
62 enum LedFrames = 25*2; // ~0.5 seconds
64 void ledUpdate (ref shared int led) nothrow @trusted @nogc {
65 import core.atomic;
66 atomicStore(led, LedFrames); // ~0.5 seconds
70 /// call this in redraw loop, ONCE!
71 /// returns alpha (i.e. 255 means "don't show")
72 public ubyte led1793Check () nothrow @trusted @nogc {
73 import core.atomic;
74 int maxled = 0;
75 int ledv;
77 void decIt(alias sym) () {
78 ledv = atomicOp!"-="(sym, 1);
79 if (ledv < 0) {
80 atomicStore(sym, 0);
81 } else {
82 if (maxled < ledv) maxled = ledv;
86 decIt!led1793Load;
87 decIt!led1793Save;
88 decIt!led1793Format;
89 decIt!led1793Seek;
91 if (maxled < 1) return 255;
92 if (maxled >= LedFrames/2) return 0; // fully opaque
93 return 255-clampToByte(255*maxled/(LedFrames/2));
97 // ////////////////////////////////////////////////////////////////////////// //
98 //#define align_by(a,b) (((ULONG_PTR)(a) + ((b)-1)) & ~((b)-1))
99 //uint align_by (uint a, uint b) pure nothrow @safe @nogc { pragma(inline, true); return ((a+(b-1))&~(b-1)); }
102 // ////////////////////////////////////////////////////////////////////////// //
103 // for WD1793 engine
104 uint wd93_crc (const(ubyte)* ptr, uint size) nothrow @trusted @nogc {
105 uint crc = 0xCDB4;
106 while (size--) {
107 crc ^= (*ptr++)<<8;
108 for (int j = 8; j; j--) // todo: rewrite with pre-calc'ed table
109 if ((crc *= 2)&0x10000) crc ^= 0x1021; // bit representation of x^12+x^5+1
111 //return _byteswap_ushort(crc); // return crc&0xFFFF;
112 crc &= 0xffff;
113 return cast(ushort)((crc>>8)|(crc<<8));
117 // for TD0
118 ushort crc16 (const(ubyte)* buf, uint size) nothrow @trusted @nogc {
119 static immutable ushort[256] crcTab = [
120 0x0000, 0x97A0, 0xB9E1, 0x2E41, 0xE563, 0x72C3, 0x5C82, 0xCB22,
121 0xCAC7, 0x5D67, 0x7326, 0xE486, 0x2FA4, 0xB804, 0x9645, 0x01E5,
122 0x032F, 0x948F, 0xBACE, 0x2D6E, 0xE64C, 0x71EC, 0x5FAD, 0xC80D,
123 0xC9E8, 0x5E48, 0x7009, 0xE7A9, 0x2C8B, 0xBB2B, 0x956A, 0x02CA,
124 0x065E, 0x91FE, 0xBFBF, 0x281F, 0xE33D, 0x749D, 0x5ADC, 0xCD7C,
125 0xCC99, 0x5B39, 0x7578, 0xE2D8, 0x29FA, 0xBE5A, 0x901B, 0x07BB,
126 0x0571, 0x92D1, 0xBC90, 0x2B30, 0xE012, 0x77B2, 0x59F3, 0xCE53,
127 0xCFB6, 0x5816, 0x7657, 0xE1F7, 0x2AD5, 0xBD75, 0x9334, 0x0494,
128 0x0CBC, 0x9B1C, 0xB55D, 0x22FD, 0xE9DF, 0x7E7F, 0x503E, 0xC79E,
129 0xC67B, 0x51DB, 0x7F9A, 0xE83A, 0x2318, 0xB4B8, 0x9AF9, 0x0D59,
130 0x0F93, 0x9833, 0xB672, 0x21D2, 0xEAF0, 0x7D50, 0x5311, 0xC4B1,
131 0xC554, 0x52F4, 0x7CB5, 0xEB15, 0x2037, 0xB797, 0x99D6, 0x0E76,
132 0x0AE2, 0x9D42, 0xB303, 0x24A3, 0xEF81, 0x7821, 0x5660, 0xC1C0,
133 0xC025, 0x5785, 0x79C4, 0xEE64, 0x2546, 0xB2E6, 0x9CA7, 0x0B07,
134 0x09CD, 0x9E6D, 0xB02C, 0x278C, 0xECAE, 0x7B0E, 0x554F, 0xC2EF,
135 0xC30A, 0x54AA, 0x7AEB, 0xED4B, 0x2669, 0xB1C9, 0x9F88, 0x0828,
136 0x8FD8, 0x1878, 0x3639, 0xA199, 0x6ABB, 0xFD1B, 0xD35A, 0x44FA,
137 0x451F, 0xD2BF, 0xFCFE, 0x6B5E, 0xA07C, 0x37DC, 0x199D, 0x8E3D,
138 0x8CF7, 0x1B57, 0x3516, 0xA2B6, 0x6994, 0xFE34, 0xD075, 0x47D5,
139 0x4630, 0xD190, 0xFFD1, 0x6871, 0xA353, 0x34F3, 0x1AB2, 0x8D12,
140 0x8986, 0x1E26, 0x3067, 0xA7C7, 0x6CE5, 0xFB45, 0xD504, 0x42A4,
141 0x4341, 0xD4E1, 0xFAA0, 0x6D00, 0xA622, 0x3182, 0x1FC3, 0x8863,
142 0x8AA9, 0x1D09, 0x3348, 0xA4E8, 0x6FCA, 0xF86A, 0xD62B, 0x418B,
143 0x406E, 0xD7CE, 0xF98F, 0x6E2F, 0xA50D, 0x32AD, 0x1CEC, 0x8B4C,
144 0x8364, 0x14C4, 0x3A85, 0xAD25, 0x6607, 0xF1A7, 0xDFE6, 0x4846,
145 0x49A3, 0xDE03, 0xF042, 0x67E2, 0xACC0, 0x3B60, 0x1521, 0x8281,
146 0x804B, 0x17EB, 0x39AA, 0xAE0A, 0x6528, 0xF288, 0xDCC9, 0x4B69,
147 0x4A8C, 0xDD2C, 0xF36D, 0x64CD, 0xAFEF, 0x384F, 0x160E, 0x81AE,
148 0x853A, 0x129A, 0x3CDB, 0xAB7B, 0x6059, 0xF7F9, 0xD9B8, 0x4E18,
149 0x4FFD, 0xD85D, 0xF61C, 0x61BC, 0xAA9E, 0x3D3E, 0x137F, 0x84DF,
150 0x8615, 0x11B5, 0x3FF4, 0xA854, 0x6376, 0xF4D6, 0xDA97, 0x4D37,
151 0x4CD2, 0xDB72, 0xF533, 0x6293, 0xA9B1, 0x3E11, 0x1050, 0x87F0,
153 uint crc = 0;
154 while (size--) crc = (crc>>8)^crcTab.ptr[(crc&0xff)^*buf++];
155 //return _byteswap_ushort(crc);
156 crc &= 0xffff;
157 return cast(ushort)((crc>>8)|(crc<<8));
161 // for UDI
162 void udi_buggy_crc (ref int crc, const(ubyte)* buf, uint len) nothrow @trusted @nogc {
163 while (len--) {
164 crc ^= -1^*buf++;
165 for (int k = 8; k--; ) { int temp = -(crc&1); crc >>= 1; crc ^= 0xEDB88320&temp; }
166 crc ^= -1;
171 // ////////////////////////////////////////////////////////////////////////// //
172 //bool done_fdd(bool Cancelable);
174 //enum Z80FQ = 3500000; // todo: #define as (wd93conf.frame*wd93conf.intfq)
175 @property uint Z80FQ () nothrow @trusted @nogc { pragma(inline, true); return Config.machine.cpuSpeed; }
176 enum FDD_RPS = 5; // rotation speed
178 enum MAX_TRACK_LEN = 6250;
179 enum MAX_CYLS = 86; // don't load images with so many tracks
180 enum MAX_PHYS_CYL = 86; // don't seek over it
181 enum MAX_SEC = 256;
184 // ////////////////////////////////////////////////////////////////////////// //
185 struct SectorHeader {
186 ubyte c, s, n, l;
187 ushort crc; // CRC заголовка сектора
188 // флаги crc зоны адреса и данных:
189 // При форматировании:
190 // 0 - генерируется правильное значение crc
191 // 1 - запись crc из crc(для адреса)/crcd(для данных)
192 // 2 - ошибочный crc (генерируется инверсией правильного crc))
193 // При чтении (функция seek устанавливает поля c1 и c2):
194 // 0 - рассчитанное crc не совпадает с указанным в заголовке (возвращается crc error)
195 // 1 - рассчитанное crc совпадает с указанным в заголовке
196 ubyte c1, c2;
197 ubyte* data; // Указатель на данные сектора внутри трэка
198 ubyte* id; // Указатель на заголовок сектора внутри трэка
199 ubyte* wp; // Указатель на битовую карту сбойных байтов сектора внутри трэка (используется только при загрузке)
200 uint wp_start; // Номер первого бита в карте сбойных байтов (относительно начала трэка) для данного сектора
201 uint datlen; // Размер сектора в байтах
202 uint crcd; // used to load specific CRC from FDI-file
206 enum SeekMode { SeekOnly = 0, LoadSectors = 1 }
209 bool test_bit (const(ubyte)* data, uint bit) nothrow @trusted @nogc {
210 return (data[bit>>3]&(1U<<(bit&7))) != 0;
213 void set_bit (ubyte* data, uint bit) nothrow @trusted @nogc {
214 data[bit>>3] |= (1U<<(bit&7));
217 void clr_bit (ubyte* data, uint bit) nothrow @trusted @nogc {
218 data[bit>>3] &= ~(1U<<(bit&7));
222 // ////////////////////////////////////////////////////////////////////////// //
223 struct TrackCache {
224 // cached track position
225 FDD drive;
226 uint cyl, side;
228 // generic track data
229 uint trklen;
230 // pointer to data inside UDI
231 ubyte* trkd; // данные
232 ubyte* trki; // битовая карта синхроимпульсов
233 ubyte* trkwp; // битовая карта сбойных байтов
234 uint ts_byte;// = Z80FQ/(MAX_TRACK_LEN*FDD_RPS); // wd93cpu.t per byte
235 SeekMode sf; // flag: is sectors filled
236 uint s; // no. of sectors
238 // sectors on track
239 SectorHeader[MAX_SEC] hdr;
241 void set (uint pos) nothrow @trusted @nogc { set_bit(trki, pos); }
242 void res (uint pos) nothrow @trusted @nogc { clr_bit(trki, pos); }
243 bool bit (uint pos) const nothrow @trusted @nogc { return test_bit(trki, pos); }
245 void setWriteProtected (uint pos) nothrow @trusted @nogc { set_bit(trkwp, pos); }
246 bool getWriteProtected (uint pos) const nothrow @trusted @nogc { return test_bit(trkwp, pos); }
248 void write (uint pos, ubyte b, byte index) nothrow @trusted @nogc {
249 if (trkd is null) return;
250 trkd[pos] = b;
251 if (index) set(pos); else res(pos);
254 void clear () nothrow @trusted @nogc {
255 drive = null;
256 trkd = null;
257 ts_byte = Z80FQ/(MAX_TRACK_LEN*FDD_RPS);
260 void seek (FDD d, uint cyl, uint side, SeekMode fs) nothrow @trusted {
261 if (d is drive && sf == fs && cyl == this.cyl && side == this.side) return;
263 drive = d;
264 sf = fs;
265 s = 0;
266 this.cyl = cyl;
267 this.side = side;
268 if (cyl >= d.cyls || !d.rawdata) { trkd = null; return; }
270 assert(cyl < MAX_CYLS);
271 trkd = d.trkd[cyl][side];
272 trki = d.trki[cyl][side];
273 trkwp = d.trkwp[cyl][side];
274 trklen = d.trklen[cyl][side];
275 if (!trklen) { trkd = null; return; }
277 ts_byte = Z80FQ/(trklen*FDD_RPS);
278 if (fs == SeekMode.SeekOnly) return; // else find sectors
280 for (uint i = 0; i < trklen-8; ++i) {
281 import std.algorithm : min;
282 if (trkd[i] != 0xA1 || trkd[i+1] != 0xFE || !bit(i)) continue; // Поиск idam
283 if (s == MAX_SEC) assert(0, "too many sectors");
284 SectorHeader* h = &hdr[s++]; // Заполнение заголовка
285 h.id = trkd+i+2; // Указатель на заголовок сектора
286 h.c = h.id[0];
287 h.s = h.id[1];
288 h.n = h.id[2];
289 h.l = h.id[3];
290 h.crc = *cast(ushort*)(trkd+i+6);
291 h.c1 = (wd93_crc(trkd+i+1, 5) == h.crc);
292 h.data = null;
293 h.datlen = 0;
294 h.wp_start = 0;
295 //if (h.l > 5) continue; [vv]
296 uint end = min(trklen-8, i+8+43); // 43-DD, 30-SD
297 // Формирование указателя на зону данных сектора
298 for (uint j = i+8; j < end; ++j) {
299 if (trkd[j] != 0xA1 || !bit(j) || bit(j+1)) continue;
300 if (trkd[j+1] == 0xF8 || trkd[j+1] == 0xFB) {
301 // Найден data am
302 h.datlen = 128<<(h.l&3); // [vv] FD1793 use only 2 lsb of sector size code
303 h.data = trkd+j+2;
304 h.c2 = (wd93_crc(h.data-1, h.datlen+1) == *cast(ushort*)(h.data+h.datlen));
305 if (trkwp) {
306 for (uint b = 0; b < h.datlen; ++b) {
307 if (getWriteProtected(j+2+b)) {
308 h.wp_start = j+2; // Есть хотябы один сбойный байт
309 break;
314 break;
319 void format () nothrow @trusted {
320 import core.stdc.string : memcpy, memset;
322 memset(trkd, 0, trklen);
323 memset(trki, 0, cast(uint)(trklen+7U)>>3);
324 memset(trkwp, 0, cast(uint)(trklen+7U)>>3);
326 ubyte* dst = trkd;
328 uint i;
330 //6250-6144=106
331 //gap4a(80)+sync0(12)+iam(3)+1+s*(gap1(50)+sync1(12)+idam(3)+1+4+2+gap2(22)+sync2(12)+data_am(3)+1+2)
332 uint gap4a = 80;
333 uint sync0 = 12;
334 uint i_am = 3;
335 uint gap1 = 40;
336 uint sync1 = 12;
337 uint id_am = 3;
338 uint gap2 = 22;
339 uint sync2 = 12;
340 uint data_am = 3;
342 uint data_sz = 0;
343 for (uint isx = 0; isx < s; isx++) {
344 SectorHeader* sechdr = hdr.ptr+isx;
345 data_sz += (128<<(sechdr.l&3)); // n
348 if ((gap4a+sync0+i_am+1+data_sz+s*(gap1+sync1+id_am+1+4+2+gap2+sync2+data_am+1+2)) >= MAX_TRACK_LEN) {
349 // Превышение стандартной длины дорожки, сокращаем параметры до минимальных
350 gap4a = 1;
351 sync0 = 1;
352 i_am = 1;
353 gap1 = 1;
354 sync1 = 1;
355 id_am = 1;
356 gap2 = 1;
357 sync2 = 1;
358 data_am = 1;
361 memset(dst, 0x4E, gap4a); dst += gap4a; // gap4a
362 memset(dst, 0, sync0); dst += sync0; //sync
364 for (i = 0; i < i_am; i++) write(dst++-trkd, 0xC2, 1); // iam
365 *dst++ = 0xFC;
367 for (uint isx = 0; isx < s; isx++) {
368 memset(dst, 0x4E, gap1); dst += gap1; // gap1 // 50 [vv] // fixme: recalculate gap1 only for non standard formats
369 memset(dst, 0, sync1); dst += sync1; //sync
370 for (i = 0; i < id_am; i++) write(dst++-trkd, 0xA1, 1); // idam
371 *dst++ = 0xFE;
373 SectorHeader* sechdr = hdr.ptr+isx;
374 *dst++ = sechdr.c; // c
375 *dst++ = sechdr.s; // h
376 *dst++ = sechdr.n; // s
377 *dst++ = sechdr.l; // n
379 uint crc = wd93_crc(dst-5, 5); // crc
380 if (sechdr.c1 == 1) crc = sechdr.crc;
381 if (sechdr.c1 == 2) crc ^= 0xFFFF;
382 *cast(uint*)dst = crc;
383 dst += 2;
385 if (sechdr.data) {
386 memset(dst, 0x4E, gap2); dst += gap2; // gap2
387 memset(dst, 0, sync2); dst += sync2; //sync
388 for (i = 0; i < data_am; i++) write(dst++-trkd, 0xA1, 1); // data am
389 *dst++ = 0xFB;
391 //if (sechdr.l > 5) errexit("strange sector"); // [vv]
392 uint len = 128<<(sechdr.l&3); // data
393 if (sechdr.data != cast(ubyte*)1) {
394 memcpy(dst, sechdr.data, len);
395 if (sechdr.wp) {
396 // Копирование битовой карты сбойных байтов
397 uint wp_start = dst-trkd;
398 sechdr.wp_start = wp_start;
399 for (uint b = 0; b < len; b++) {
400 if (test_bit(sechdr.wp, b)) setWriteProtected(wp_start+b);
403 } else {
404 memset(dst, 0, len);
407 crc = wd93_crc(dst-1, len+1); // crc
408 if (sechdr.c2 == 1) crc = sechdr.crcd;
409 if (sechdr.c2 == 2) crc ^= 0xFFFF;
410 *cast(uint*)(dst+len) = crc;
411 dst += len+2;
414 if (dst > trklen+trkd) {
415 //printf("cyl=%u, h=%u, additional len=%u\n", cyl, side, dst-(trklen+trkd));
416 //errexit("track too long");
417 assert(0);
419 while (dst < trkd+trklen) *dst++ = 0x4E;
422 int writeSector (uint sec, const(ubyte)* data) nothrow @trusted {
423 import core.stdc.string : memcpy;
424 SectorHeader* h = getSector(sec);
425 if (h is null || !h.data) return 0;
426 uint sz = h.datlen;
427 if (h.data != data) memcpy(h.data, data, sz);
428 *cast(ushort*)(h.data+sz) = cast(ushort)wd93_crc(h.data-1, sz+1);
429 return sz;
432 SectorHeader* getSector (uint sec) nothrow @trusted {
433 uint i;
434 for (i = 0; i < s; i++) if (hdr[i].n == sec) break;
435 if (i == s) return null;
436 if (/*(hdr[i].l&3) != 1 ||*/ hdr[i].c != cyl) return null; // [vv]
437 return &hdr[i];
442 // ////////////////////////////////////////////////////////////////////////// //
443 public class WD1793 {
444 enum WDSTATE {
445 S_IDLE = 0,
446 S_WAIT,
448 S_DELAY_BEFORE_CMD,
449 S_CMD_RW,
450 S_FOUND_NEXT_ID,
451 S_RDSEC,
452 S_READ,
453 S_WRSEC,
454 S_WRITE,
455 S_WRTRACK,
456 S_WR_TRACK_DATA,
458 S_TYPE1_CMD,
459 S_STEP,
460 S_SEEKSTART,
461 S_RESTORE,
462 S_SEEK,
463 S_VERIFY,
464 S_VERIFY2,
466 S_WAIT_HLT,
467 S_WAIT_HLT_RW,
469 S_RESET
472 long next, time;
473 long idx_tmo;
475 FDD seldrive;
476 uint tshift;
478 WDSTATE state, state2;
480 ubyte cmd;
481 ubyte data, track, sector;
482 ubyte rqs, status;
483 ubyte sign_status; // Внешние сигналы (пока только HLD)
485 uint drive, side; // update this with changing 'system'
487 byte stepdirection;
488 ubyte system; // beta128 system register
490 uint idx_cnt; // idx counter
492 // read/write sector(s) data
493 long end_waiting_am;
494 uint foundid; // index in trkcache.hdr for next encountered ID and bytes before this ID
495 uint rwptr, rwlen;
497 // format track data
498 uint start_crc;
500 enum CMDBITS : ubyte {
501 SEEK_RATE = 0x03,
502 SEEK_VERIFY = 0x04,
503 SEEK_HEADLOAD = 0x08,
504 SEEK_TRKUPD = 0x10,
505 SEEK_DIR = 0x20,
507 WRITE_DEL = 0x01,
508 SIDE_CMP_FLAG = 0x02,
509 DELAY = 0x04,
510 SIDE = 0x08,
511 SIDE_SHIFT = 3,
512 MULTIPLE = 0x10,
515 enum BETA_STATUS : ubyte {
516 DRQ = 0x40,
517 INTRQ = 0x80,
520 enum WD_STATUS : ubyte {
521 WDS_BUSY = 0x01,
522 WDS_INDEX = 0x02,
523 WDS_DRQ = 0x02,
524 WDS_TRK00 = 0x04,
525 WDS_LOST = 0x04,
526 WDS_CRCERR = 0x08,
527 WDS_NOTFOUND = 0x10,
528 WDS_SEEKERR = 0x10,
529 WDS_RECORDT = 0x20,
530 WDS_HEADL = 0x20,
531 WDS_WRFAULT = 0x20,
532 WDS_WRITEP = 0x40,
533 WDS_NOTRDY = 0x80
536 enum WD_SYS : ubyte {
537 SYS_HLT = 0x08
540 enum WD_SIG : ubyte {
541 SIG_HLD = 0x01
544 FDD[4] fdd;
546 public:
547 // ctor
548 this () {
549 foreach (ubyte i; 0..4) fdd[i] = new FDD(i);
550 for (ubyte i = 0; i < 4; i++) fdd[i].Id = i; // [vv] Для удобства отладки
551 seldrive = fdd[0];
552 idx_cnt = 0;
553 idx_tmo = long.max;
554 sign_status = 0;
557 private void process () nothrow @trusted {
558 import core.stdc.string : memcpy;
560 time = totalTS;
562 if (!(sign_status&WD_SIG.SIG_HLD) && !(system&0x20)) seldrive.motor = 0;
564 if (seldrive.rawdata) status &= ~cast(uint)WD_STATUS.WDS_NOTRDY; else status |= WD_STATUS.WDS_NOTRDY;
566 if (!(cmd&0x80) || (cmd&0xF0) == 0xD0) {
567 // seek/step commands
568 status &= ~cast(uint)WD_STATUS.WDS_INDEX;
570 if (state != WDSTATE.S_IDLE) {
571 status &= ~cast(uint)(WD_STATUS.WDS_TRK00|WD_STATUS.WDS_INDEX);
572 if (!seldrive.track) status |= WD_STATUS.WDS_TRK00;
575 // todo: test spinning
576 if (seldrive.rawdata && seldrive.motor && ((time+tshift)%(Z80FQ/FDD_RPS) < (Z80FQ*4/1000))) {
577 // index every turn, len=4ms (if disk present)
578 if (state == WDSTATE.S_IDLE) {
579 if (time < idx_tmo) status |= WD_STATUS.WDS_INDEX;
580 } else {
581 status |= WD_STATUS.WDS_INDEX;
586 for (;;) {
587 if (!seldrive.motor) status |= WD_STATUS.WDS_NOTRDY; else status &= ~cast(uint)WD_STATUS.WDS_NOTRDY;
588 switch (state) {
589 // ----------------------------------------------------
590 case WDSTATE.S_IDLE:
591 status &= ~cast(uint)WD_STATUS.WDS_BUSY;
592 if (idx_cnt >= 15 || time > idx_tmo) {
593 idx_cnt = 15;
594 status &= WD_STATUS.WDS_NOTRDY;
595 status |= WD_STATUS.WDS_NOTRDY;
596 seldrive.motor = 0;
597 sign_status &= ~cast(uint)WD_SIG.SIG_HLD;
599 rqs = BETA_STATUS.INTRQ;
600 return;
602 case WDSTATE.S_WAIT:
603 if (time < next) return;
604 state = state2;
605 break;
607 // ----------------------------------------------------
608 // Подана команда типа 2 или 3 (read/write)/(read am/read trk/write trk)
609 case WDSTATE.S_DELAY_BEFORE_CMD:
610 if (!Config.WD93.noDelay && (cmd&CMDBITS.DELAY)) { // Проверка бита E=1
611 next += (Z80FQ*15/1000); // 15ms delay
613 state2 = WDSTATE.S_WAIT_HLT_RW;
614 state = WDSTATE.S_WAIT;
615 break;
617 case WDSTATE.S_WAIT_HLT_RW:
618 if (!(system&0x08)) return; // HLT = 0 (бесконечное ожидание HLT)
619 state = WDSTATE.S_CMD_RW;
620 goto case;
622 case WDSTATE.S_CMD_RW:
623 if (((cmd&0xE0) == 0xA0 || (cmd&0xF0) == 0xF0) && Config.WD93.writeProt(drive)) {
624 status |= WD_STATUS.WDS_WRITEP;
625 state = WDSTATE.S_IDLE;
626 break;
629 if ((cmd&0xC0) == 0x80 || (cmd&0xF8) == 0xC0) {
630 // read/write sectors or read am-find next AM
631 end_waiting_am = next+5*Z80FQ/FDD_RPS; // max wait disk 5 turns
632 findMarker();
633 break;
636 if ((cmd&0xF8) == 0xF0) {
637 // write track
638 rqs = BETA_STATUS.DRQ;
639 status |= WD_STATUS.WDS_DRQ;
640 next += 3*seldrive.t.ts_byte;
641 state2 = WDSTATE.S_WRTRACK;
642 state = WDSTATE.S_WAIT;
643 break;
646 if ((cmd&0xF8) == 0xE0) {
647 // read track
648 load();
649 rwptr = 0;
650 rwlen = seldrive.t.trklen;
651 state2 = WDSTATE.S_READ;
652 getIndex();
653 break;
656 // else unknown command
657 state = WDSTATE.S_IDLE;
658 break;
660 case WDSTATE.S_FOUND_NEXT_ID:
661 if (!seldrive.rawdata) {
662 // no disk-wait again
663 end_waiting_am = next+5*Z80FQ/FDD_RPS;
664 nextmk:
665 findMarker();
666 break;
669 if (next >= end_waiting_am) {
671 status |= WD_STATUS.WDS_NOTFOUND;
672 state = WDSTATE.S_IDLE;
673 break;
676 if (foundid == -1) goto nf;
678 status &= ~cast(uint)WD_STATUS.WDS_CRCERR;
679 load();
681 if (!(cmd&0x80)) {
682 // verify after seek
683 if (seldrive.t.hdr[foundid].c != track) goto nextmk;
684 if (!seldrive.t.hdr[foundid].c1) {
685 status |= WD_STATUS.WDS_CRCERR;
686 goto nextmk;
688 state = WDSTATE.S_IDLE; break;
691 if ((cmd&0xF0) == 0xC0) {
692 // read AM
693 rwptr = seldrive.t.hdr[foundid].id-seldrive.t.trkd;
694 rwlen = 6;
696 read_first_byte:
697 ubyte data_mask = 0;
698 if (seldrive.t.trkwp) {
699 // проверяем карту сбойных байтов
700 if (seldrive.t.getWriteProtected(rwptr)) {
701 data_mask = 0xFF;
702 seldrive.t.hdr[foundid].c1 = 0; // bad address crc
705 data = seldrive.t.trkd[rwptr++];
706 data ^= data_mask;
707 rwlen--;
708 rqs = BETA_STATUS.DRQ;
709 status |= WD_STATUS.WDS_DRQ;
710 next += seldrive.t.ts_byte;
711 state = WDSTATE.S_WAIT;
712 state2 = WDSTATE.S_READ;
713 break;
716 // else R/W sector(s)
717 if (seldrive.t.hdr[foundid].c != track || seldrive.t.hdr[foundid].n != sector) goto nextmk;
718 if ((cmd&CMDBITS.SIDE_CMP_FLAG) && (((cmd>>CMDBITS.SIDE_SHIFT)^seldrive.t.hdr[foundid].s)&1)) goto nextmk;
719 if (!seldrive.t.hdr[foundid].c1) { status |= WD_STATUS.WDS_CRCERR; goto nextmk; }
721 if (cmd&0x20) {
722 // write sector(s)
723 rqs = BETA_STATUS.DRQ;
724 status |= WD_STATUS.WDS_DRQ;
725 next += seldrive.t.ts_byte*9;
726 state = WDSTATE.S_WAIT; state2 = WDSTATE.S_WRSEC;
727 break;
730 // read sector(s)
731 if (!seldrive.t.hdr[foundid].data) goto nextmk; // Сектор без зоны данных
732 if (!Config.WD93.noDelay) next += seldrive.t.ts_byte*(seldrive.t.hdr[foundid].data-seldrive.t.hdr[foundid].id); // Задержка на пропуск заголовка сектора и пробела между заголовком и зоной данных
733 state = WDSTATE.S_WAIT;
734 state2 = WDSTATE.S_RDSEC;
735 break;
737 case WDSTATE.S_RDSEC:
738 if (seldrive.t.hdr[foundid].data[-1] == 0xF8) status |= WD_STATUS.WDS_RECORDT; else status &= ~cast(uint)WD_STATUS.WDS_RECORDT;
739 rwptr = seldrive.t.hdr[foundid].data-seldrive.t.trkd; // Смещение зоны данных сектора (в байтах) относительно начала трека
740 rwlen = 128<<(seldrive.t.hdr[foundid].l&3); // [vv]
741 goto read_first_byte;
743 case WDSTATE.S_READ:
744 if (notReady) break;
745 load();
747 if (!seldrive.t.trkd) {
748 status |= WD_STATUS.WDS_NOTFOUND;
749 state = WDSTATE.S_IDLE;
750 break;
753 if (rwlen) {
754 ledUpdate(led1793Load);
755 if (rqs&BETA_STATUS.DRQ) status |= WD_STATUS.WDS_LOST;
757 ubyte data_mask = 0;
758 if (seldrive.t.trkwp) {
759 // проверяем карту сбойных байтов
760 if (seldrive.t.getWriteProtected(rwptr)) {
761 data_mask = 0xFF;
762 if ((cmd&0xE0) == 0x80) {
763 // read sector
764 seldrive.t.hdr[foundid].c2 = 0; // bad data crc
766 if ((cmd&0xF0) == 0xC0) {
767 // read address
768 seldrive.t.hdr[foundid].c1 = 0; // bad address crc
772 data = seldrive.t.trkd[rwptr++];
773 data ^= data_mask;
774 rwlen--;
775 rqs = BETA_STATUS.DRQ;
776 status |= WD_STATUS.WDS_DRQ;
778 if (!Config.WD93.noDelay) next += seldrive.t.ts_byte; else next = time+1;
779 state = WDSTATE.S_WAIT;
780 state2 = WDSTATE.S_READ;
781 } else {
782 if ((cmd&0xE0) == 0x80) {
783 // read sector
784 if (!seldrive.t.hdr[foundid].c2) status |= WD_STATUS.WDS_CRCERR;
785 if (cmd&CMDBITS.MULTIPLE) {
786 sector++;
787 state = WDSTATE.S_CMD_RW;
788 break;
792 // FIXME: Временный хак для zx-format 3
793 if (((cmd&0xF8) == 0xE0) && (seldrive.t.trklen < MAX_TRACK_LEN)) {
794 // read track
795 status |= WD_STATUS.WDS_LOST;
798 if ((cmd&0xF0) == 0xC0) {
799 // read address
800 if (!seldrive.t.hdr[foundid].c1) status |= WD_STATUS.WDS_CRCERR;
802 state = WDSTATE.S_IDLE;
804 break;
806 case WDSTATE.S_WRSEC:
807 load();
808 if (rqs&BETA_STATUS.DRQ) {
809 status |= WD_STATUS.WDS_LOST;
810 state = WDSTATE.S_IDLE;
811 break;
813 seldrive.optype |= 1;
814 rwptr = seldrive.t.hdr[foundid].id+6+11+11-seldrive.t.trkd;
815 for (rwlen = 0; rwlen < 12; rwlen++) seldrive.t.write(rwptr++, 0, 0);
816 for (rwlen = 0; rwlen < 3; rwlen++) seldrive.t.write(rwptr++, 0xA1, 1);
817 seldrive.t.write(rwptr++, (cmd&CMDBITS.WRITE_DEL ? 0xF8 : 0xFB), 0);
818 rwlen = 128<<(seldrive.t.hdr[foundid].l&3); // [vv]
819 state = WDSTATE.S_WRITE;
820 break;
822 case WDSTATE.S_WRITE:
823 if (notReady) break;
824 if (rqs&BETA_STATUS.DRQ) status |= WD_STATUS.WDS_LOST, data = 0;
825 ledUpdate(led1793Save);
826 seldrive.t.write(rwptr++, data, 0); rwlen--;
827 if (rwptr == seldrive.t.trklen) rwptr = 0;
828 seldrive.t.sf = SeekMode.SeekOnly; // invalidate sectors
829 if (rwlen) {
830 if (!Config.WD93.noDelay) next += seldrive.t.ts_byte;
831 state = WDSTATE.S_WAIT; state2 = WDSTATE.S_WRITE;
832 rqs = BETA_STATUS.DRQ; status |= WD_STATUS.WDS_DRQ;
833 } else {
834 uint len = (128<<(seldrive.t.hdr[foundid].l&3))+1; //[vv]
835 ubyte[2056] sc = void;
836 if (rwptr < len) {
837 memcpy(sc.ptr, seldrive.t.trkd+seldrive.t.trklen-rwptr, rwptr);
838 memcpy(sc.ptr+rwptr, seldrive.t.trkd, len-rwptr);
839 } else {
840 memcpy(sc.ptr, seldrive.t.trkd+rwptr-len, len);
842 uint crc = wd93_crc(sc.ptr, len);
843 seldrive.t.write(rwptr++, cast(ubyte)crc, 0);
844 seldrive.t.write(rwptr++, cast(ubyte)(crc>>8), 0);
845 seldrive.t.write(rwptr, 0xFF, 0);
846 if (cmd&CMDBITS.MULTIPLE) {
847 sector++;
848 state = WDSTATE.S_CMD_RW;
849 break;
851 state = WDSTATE.S_IDLE;
853 break;
855 case WDSTATE.S_WRTRACK:
856 if (rqs&BETA_STATUS.DRQ) {
857 status |= WD_STATUS.WDS_LOST;
858 state = WDSTATE.S_IDLE;
859 break;
861 seldrive.optype |= 2;
862 state2 = WDSTATE.S_WR_TRACK_DATA;
863 start_crc = 0;
864 getIndex();
865 end_waiting_am = next+5*Z80FQ/FDD_RPS;
866 break;
868 case WDSTATE.S_WR_TRACK_DATA:
869 if (notReady) break;
870 ledUpdate(led1793Format);
871 if (rqs&BETA_STATUS.DRQ) {
872 status |= WD_STATUS.WDS_LOST;
873 data = 0;
875 seldrive.t.seek(seldrive, seldrive.track, side, SeekMode.SeekOnly);
876 seldrive.t.sf = SeekMode.SeekOnly; // invalidate sectors
878 if (!seldrive.t.trkd) {
879 state = WDSTATE.S_IDLE;
880 break;
883 ubyte marker = 0, byt = data;
884 uint crc;
885 switch (data) {
886 case 0xF5:
887 byt = 0xA1;
888 marker = 1;
889 start_crc = rwptr+1;
890 break;
891 case 0xF6:
892 byt = 0xC2;
893 marker = 1;
894 break;
895 case 0xF7:
896 crc = wd93_crc(seldrive.t.trkd+start_crc, rwptr-start_crc);
897 byt = crc&0xFF;
898 break;
899 default: break;
902 seldrive.t.write(rwptr++, byt, marker);
903 rwlen--;
904 if (data == 0xF7) {
905 seldrive.t.write(rwptr++, (crc>>8)&0xFF, 0);
906 rwlen--; // second byte of CRC16
909 if (cast(int)rwlen > 0) {
910 if (!Config.WD93.noDelay) next += seldrive.t.ts_byte;
911 state2 = WDSTATE.S_WR_TRACK_DATA;
912 state = WDSTATE.S_WAIT;
913 rqs = BETA_STATUS.DRQ;
914 status |= WD_STATUS.WDS_DRQ;
915 break;
917 state = WDSTATE.S_IDLE;
918 break;
920 // ----------------------------------------------------
921 case WDSTATE.S_TYPE1_CMD: // Подана команда типа 1 (restore/seek/step)
922 status &= ~cast(uint)(WD_STATUS.WDS_CRCERR|WD_STATUS.WDS_SEEKERR|WD_STATUS.WDS_WRITEP);
923 rqs = 0;
925 if (Config.WD93.writeProt(drive)) status |= WD_STATUS.WDS_WRITEP;
927 seldrive.motor = (cmd&CMDBITS.SEEK_HEADLOAD) || (system&0x20 ? next+2*Z80FQ : 0);
929 state2 = WDSTATE.S_SEEKSTART; // default is seek/restore
931 if (cmd&0xE0) {
932 // single step
933 if (cmd&0x40) stepdirection = (cmd&CMDBITS.SEEK_DIR ? -1 : 1); // step in/step out
934 state2 = WDSTATE.S_STEP;
937 next += (Z80FQ*14)/1000000;
938 state = WDSTATE.S_WAIT; // Задержка 14мкс перед появленеим статуса BUSY
939 break;
941 case WDSTATE.S_STEP:
942 status |= WD_STATUS.WDS_BUSY;
943 ledUpdate(led1793Seek);
945 if (seldrive.track == 0 && stepdirection < 0) {
946 // проверка TRK00
947 track = 0;
948 state = WDSTATE.S_VERIFY; // Обработка бита команды V
949 break;
952 // Обработкабита T=1 (для seek всегда 1, для restore всегда 0, но обновление регистра трэка делается)
953 if (!(cmd&0xF0) || (cmd&CMDBITS.SEEK_TRKUPD)) track += stepdirection;
955 if (seldrive.motor) {
956 seldrive.track += stepdirection;
957 if (seldrive.track == cast(ubyte)-1) seldrive.track = 0;
958 if (seldrive.track >= MAX_PHYS_CYL) seldrive.track = MAX_PHYS_CYL;
959 seldrive.t.clear();
962 static immutable uint[4] steps = [ 6, 12, 20, 30 ];
963 if (!Config.WD93.noDelay) next += steps[cmd&CMDBITS.SEEK_RATE]*Z80FQ/1000;
965 version(MOD_9X) {
966 if (!Config.WD93.noDelay && wd93conf.fdd_noise) Beep((stepdirection > 0? 600 : 800), 2);
969 state2 = (cmd&0xE0 ? WDSTATE.S_VERIFY /*Команда step*/: WDSTATE.S_SEEK /*Команда seek/restore*/);
970 state = WDSTATE.S_WAIT;
971 break;
973 case WDSTATE.S_SEEKSTART:
974 status |= WD_STATUS.WDS_BUSY;
975 if (!(cmd&0x10)) {
976 // Команда restore
977 if (!Config.WD93.noDelay) {
978 state2 = WDSTATE.S_RESTORE;
979 next += (Z80FQ*21)/1000000; // задержка ~21мкс перед загрузкой регистра трэка
980 state = WDSTATE.S_WAIT;
981 break;
983 state = WDSTATE.S_RESTORE;
984 break;
986 state = WDSTATE.S_SEEK;
987 break;
989 case WDSTATE.S_RESTORE:
990 track = 0xFF;
991 data = 0;
992 state = WDSTATE.S_SEEK;
993 break;
995 case WDSTATE.S_SEEK:
996 if (data == track) {
997 state = WDSTATE.S_VERIFY;
998 break;
1000 stepdirection = (data < track ? -1 : 1);
1001 state = WDSTATE.S_STEP;
1002 break;
1004 case WDSTATE.S_VERIFY:
1005 if (!(cmd&CMDBITS.SEEK_VERIFY)) {
1006 // Проверка номера трэка не нужна V=0
1007 status |= WD_STATUS.WDS_BUSY;
1008 state2 = WDSTATE.S_IDLE;
1009 state = WDSTATE.S_WAIT;
1010 idx_tmo = next+15*Z80FQ/FDD_RPS; // 15 disk turns
1011 next += (105*Z80FQ)/1000000; // Задержка 105мкс со статусом BUSY
1012 break;
1015 // Проверка номера трэка нужна V=1
1016 sign_status |= WD_SIG.SIG_HLD;
1018 // Ожидание 15мс
1019 // |
1020 // v
1021 // Ожидание HLT == 1
1022 // |
1023 // v
1024 // S_VERIFY2
1025 if (!Config.WD93.noDelay) {
1026 next += (15*Z80FQ)/1000; // Задержка 15мс
1027 state2 = WDSTATE.S_WAIT_HLT;
1028 state = WDSTATE.S_WAIT;
1029 break;
1031 state = WDSTATE.S_WAIT_HLT;
1032 goto case;
1034 case WDSTATE.S_WAIT_HLT:
1035 if (!(system&0x08)) return; // HLT = 0 (бесконечное ожидание HLT)
1036 state = WDSTATE.S_VERIFY2;
1037 goto case;
1039 case WDSTATE.S_VERIFY2:
1040 end_waiting_am = next+6*Z80FQ/FDD_RPS; // max wait disk 6 turns
1041 load();
1042 findMarker();
1043 break;
1045 // ----------------------------------------------------
1046 case WDSTATE.S_RESET: // seek to trk0, but don't be busy
1047 if (!seldrive.track) {
1048 state = WDSTATE.S_IDLE;
1049 } else {
1050 seldrive.track--;
1051 seldrive.t.clear();
1053 // if (!seldrive.track) track = 0;
1054 next += 6*Z80FQ/1000;
1055 break;
1057 default:
1058 assert(0, "WD1793 in wrong state");
1063 private void findMarker () nothrow @trusted {
1064 if (Config.WD93.noDelay && seldrive.track != track) seldrive.track = track;
1065 load();
1067 foundid = -1;
1068 if (seldrive.motor && seldrive.rawdata) {
1069 uint div = seldrive.t.trklen*seldrive.t.ts_byte; // Длина дорожки в тактах wd93cpu
1070 uint i = cast(uint)((next+tshift)%div)/seldrive.t.ts_byte; // Позиция байта соответствующего текущему такту на дорожке
1071 uint wait = -1;
1073 // Поиск заголовка минимально отстоящего от текущего байта
1074 for (uint isx = 0; isx < seldrive.t.s; isx++) {
1075 uint pos = seldrive.t.hdr[isx].id-seldrive.t.trkd; // Смещение (в байтах) заголовка относительно начала дорожки
1076 uint dist = (pos > i ? pos-i : seldrive.t.trklen+pos-i); // Расстояние (в байтах) от заголовка до текущего байта
1077 if (dist < wait) {
1078 wait = dist;
1079 foundid = isx;
1083 if (foundid != -1)
1084 wait *= seldrive.t.ts_byte; // Задержка в тактах от текущего такта до такта чтения первого байта заголовка
1085 else
1086 wait = 10*Z80FQ/FDD_RPS;
1088 if (Config.WD93.noDelay && foundid != -1) {
1089 // adjust tshift, that id appares right under head
1090 uint pos = seldrive.t.hdr[foundid].id-seldrive.t.trkd+2;
1091 tshift = cast(uint)(((pos*seldrive.t.ts_byte)-(next%div)+div)%div);
1092 wait = 100; // delay=0 causes fdc to search infinitely, when no matched id on track
1095 next += wait;
1096 } else {
1097 // else no index pulses - infinite wait
1098 next = totalTS+1;
1101 if (seldrive.rawdata && next > end_waiting_am) {
1102 next = end_waiting_am;
1103 foundid = -1;
1105 state = WDSTATE.S_WAIT;
1106 state2 = WDSTATE.S_FOUND_NEXT_ID;
1109 private bool notReady () nothrow @trusted {
1110 // fdc is too fast in no-delay mode, wait until cpu handles DRQ, but not more 'end_waiting_am'
1111 if (Config.WD93.noDelay && (rqs&BETA_STATUS.DRQ) && (next < (end_waiting_am-5*Z80FQ/FDD_RPS)+(600*Z80FQ)/1000)) {
1112 state2 = state;
1113 state = WDSTATE.S_WAIT;
1114 next += seldrive.t.ts_byte;
1115 return true;
1117 return false;
1120 private void getIndex () nothrow @trusted {
1121 uint trlen = seldrive.t.trklen*seldrive.t.ts_byte;
1122 uint ticks = cast(uint)((next+tshift)%trlen);
1123 if (!Config.WD93.noDelay) next += (trlen-ticks);
1124 rwptr = 0;
1125 rwlen = seldrive.t.trklen;
1126 state = WDSTATE.S_WAIT;
1129 void load () nothrow @trusted {
1130 seldrive.t.seek(seldrive, seldrive.track, side, SeekMode.LoadSectors);
1133 ubyte inp (ubyte port) nothrow @trusted {
1134 process();
1135 if (port&0x80) return rqs|(system&0x3F);
1136 if (port == 0x1F) {
1137 rqs &= ~cast(uint)BETA_STATUS.INTRQ;
1138 return rdStatus();
1140 if (port == 0x3F) return track;
1141 if (port == 0x5F) return sector;
1142 if (port == 0x7F) {
1143 status &= ~cast(uint)WD_STATUS.WDS_DRQ;
1144 rqs &= ~cast(uint)BETA_STATUS.DRQ;
1145 return data;
1147 return 0xFF;
1150 ubyte rdStatus () nothrow @trusted {
1151 if (!(cmd&0x80)) {
1152 // hld & hlt
1153 return status|((sign_status&WD_SIG.SIG_HLD) && (system&8) ? WD_STATUS.WDS_HEADL : 0);
1155 return status;
1158 void outp (ubyte port, ubyte val) nothrow @trusted {
1159 process();
1161 if (port == 0x1F) {
1162 // cmd
1164 // force interrupt (type 4)
1165 if ((val&0xF0) == 0xD0) {
1166 ubyte Cond = (val&0xF);
1167 next = totalTS;
1168 idx_cnt = 0;
1169 idx_tmo = next+15*Z80FQ/FDD_RPS; // 15 disk turns
1170 cmd = val;
1172 if (Cond == 0) {
1173 state = WDSTATE.S_IDLE; rqs = 0;
1174 status &= ~cast(uint)WD_STATUS.WDS_BUSY;
1175 return;
1178 if (Cond&8) {
1179 // unconditional int
1180 state = WDSTATE.S_IDLE; rqs = BETA_STATUS.INTRQ;
1181 status &= ~cast(uint)WD_STATUS.WDS_BUSY;
1182 return;
1185 if (Cond&4) {
1186 // int by idam (unimplemented yet)
1187 state = WDSTATE.S_IDLE; rqs = BETA_STATUS.INTRQ;
1188 status &= ~cast(uint)WD_STATUS.WDS_BUSY;
1189 return;
1192 if (Cond&2) {
1193 // int 1.0 rdy (unimplemented yet)
1194 state = WDSTATE.S_IDLE; rqs = BETA_STATUS.INTRQ;
1195 status &= ~cast(uint)WD_STATUS.WDS_BUSY;
1196 return;
1199 if (Cond&1) {
1200 // int 0.1 rdy (unimplemented yet)
1201 state = WDSTATE.S_IDLE; rqs = BETA_STATUS.INTRQ;
1202 status &= ~cast(uint)WD_STATUS.WDS_BUSY;
1203 return;
1206 return;
1209 if (status&WD_STATUS.WDS_BUSY) return;
1210 cmd = val;
1211 next = totalTS;
1212 status |= WD_STATUS.WDS_BUSY;
1213 rqs = 0;
1214 idx_cnt = 0;
1215 idx_tmo = long.max;
1217 //-----------------------------------------------------------------------
1219 if (cmd&0x80) {
1220 // read/write command (type 2, 3)
1221 status = (status|WD_STATUS.WDS_BUSY)&~cast(uint)(WD_STATUS.WDS_DRQ|WD_STATUS.WDS_LOST|WD_STATUS.WDS_NOTFOUND|WD_STATUS.WDS_RECORDT|WD_STATUS.WDS_WRITEP);
1223 // continue disk spinning
1224 seldrive.motor = next+2*Z80FQ;
1226 // abort if no disk
1227 if (status&WD_STATUS.WDS_NOTRDY) {
1228 state2 = WDSTATE.S_IDLE;
1229 state = WDSTATE.S_WAIT;
1230 next = totalTS+Z80FQ/FDD_RPS;
1231 rqs = BETA_STATUS.INTRQ;
1232 return;
1235 sign_status |= WD_SIG.SIG_HLD;
1237 state = WDSTATE.S_DELAY_BEFORE_CMD;
1238 return;
1241 // (type 1)
1242 if (cmd&CMDBITS.SEEK_HEADLOAD) // h = 1
1243 sign_status |= WD_SIG.SIG_HLD;
1244 else
1245 sign_status &= ~cast(uint)WD_SIG.SIG_HLD;
1247 // else seek/step command
1248 status &= ~cast(uint)WD_STATUS.WDS_BUSY;
1249 state = WDSTATE.S_TYPE1_CMD;
1250 return;
1253 //=======================================================================
1255 if (port == 0x3F) {
1256 track = val;
1257 return;
1260 if (port == 0x5F) {
1261 sector = val;
1262 return;
1265 if (port == 0x7F) {
1266 data = val;
1267 rqs &= ~cast(uint)BETA_STATUS.DRQ;
1268 status &= ~cast(uint)WD_STATUS.WDS_DRQ;
1269 return;
1272 if (port&0x80) {
1273 // FF
1274 // system
1275 drive = val&3;
1276 side = ~(val>>4)&1;
1277 seldrive = fdd[drive];
1278 seldrive.t.clear();
1280 if (!(val&0x04)) {
1281 // reset
1282 status = WD_STATUS.WDS_NOTRDY;
1283 rqs = BETA_STATUS.INTRQ;
1284 seldrive.motor = 0;
1285 state = WDSTATE.S_IDLE;
1286 idx_cnt = 0;
1287 version(all) { // move head to trk00
1288 //steptime = 6*(Z80FQ/1000); // 6ms
1289 next += 1*Z80FQ/1000; // 1ms before command
1290 state = WDSTATE.S_RESET;
1291 //seldrive.track = 0;
1293 } else {
1294 if ((system^val)&WD_SYS.SYS_HLT) {
1295 // hlt 0.1
1296 if (!(status&WD_STATUS.WDS_BUSY)) idx_cnt++;
1300 if (val&0x20) {
1301 // В quorum бит D5 управляет мотором дисковода
1302 sign_status |= WD_SIG.SIG_HLD;
1303 seldrive.motor = next+2*Z80FQ;
1305 system = val;
1309 void trdosTraps () nothrow @trusted {
1310 uint pc = z80pc;
1311 if (pc < 0x3DFD) return;
1313 // Позиционирование на соседнюю дорожку (пауза)
1314 if (pc == 0x3DFD && trdosROMread(0x3DFD) == 0x3E && trdosROMread(0x3DFF) == 0x0E) {
1315 if (trdTrapsDebug) conwriteln("TRDTRAP: next track");
1316 z80pc = z80pop();
1317 emuz80.AF.a = 0;
1318 emuz80.BC.c = 0;
1321 // Позиционирование на произвольную дорожку (пауза)
1322 if (pc == 0x3EA0 && trdosROMread(0x3EA0) == 0x06 && trdosROMread(0x3EA2) == 0x3E) {
1323 if (trdTrapsDebug) conwriteln("TRDTRAP: seek track");
1324 z80pc = z80pop();
1325 emuz80.AF.a = 0;
1326 emuz80.BC.b = 0;
1329 if (pc == 0x3E01 && trdosROMread(0x3E01) == 0x0D) {
1330 if (trdTrapsDebug) conwriteln("TRDTRAP: delay skip");
1331 emuz80.AF.a = emuz80.BC.c = 1;
1332 return;
1333 } // no delays
1335 if (pc == 0x3FEC && trdosROMread(0x3FED) == 0xA2 && (state == WDSTATE.S_READ || (state2 == WDSTATE.S_READ && state == WDSTATE.S_WAIT))) {
1336 if (trdTrapsDebug) conwriteln("TRDTRAP: read byte");
1337 ledUpdate(led1793Load);
1338 if (rqs&BETA_STATUS.DRQ) {
1339 emuz80.memPokeB(emuz80.HL, data);
1340 //wd93cpu.DbgMemIf.wm(wd93cpu.hl, data); // move byte from controller
1341 emuz80.HL++;
1342 emuz80.BC.b--;
1343 rqs &= ~cast(uint)BETA_STATUS.DRQ;
1344 status &= ~cast(uint)WD_STATUS.WDS_DRQ;
1346 if (seldrive.t.trkd) {
1347 while (rwlen) {
1348 // move others
1349 emuz80.memPokeB(emuz80.HL, seldrive.t.trkd[rwptr++]);
1350 //wd93cpu.DbgMemIf.wm(wd93cpu.hl, seldrive.t.trkd[rwptr++]);
1351 rwlen--;
1352 emuz80.HL++;
1353 emuz80.BC.b--;
1356 z80pc = z80pc+2; // skip INI
1357 return;
1360 if (pc == 0x3FD1 && trdosROMread(0x3FD2) == 0xA3 && (rqs&BETA_STATUS.DRQ) && (rwlen>1) && (state == WDSTATE.S_WRITE || (state2 == WDSTATE.S_WRITE && state == WDSTATE.S_WAIT))) {
1361 if (trdTrapsDebug) conwriteln("TRDTRAP: write byte");
1362 ledUpdate(led1793Save);
1363 while (rwlen > 1) {
1364 //seldrive.t.write(rwptr++, wd93cpu.DbgMemIf.rm(wd93cpu.hl), 0);
1365 seldrive.t.write(rwptr++, emuz80.memPeekB(emuz80.HL), 0);
1366 rwlen--;
1367 emuz80.HL++;
1368 emuz80.BC.b--;
1370 z80pc = z80pc+2; // skip OUTI
1371 return;
1377 // ////////////////////////////////////////////////////////////////////////// //
1378 public class FDD {
1379 ubyte Id;
1380 // drive data
1382 long motor; // 0 - not spinning, >0 - time when it'll stop
1383 ubyte track; // head position
1385 // disk data
1386 ubyte* rawdata; // used in VirtualAlloc/VirtualFree
1387 uint rawsize;
1388 uint cyls, sides;
1389 uint[2][MAX_CYLS] trklen;
1390 ubyte*[2][MAX_CYLS] trkd; // данные
1391 ubyte*[2][MAX_CYLS] trki; // битовые карты синхроимпульсов
1392 ubyte*[2][MAX_CYLS] trkwp; // битовые карты сбойных байтов
1393 ubyte optype; // bits: 0-not modified, 1-write sector, 2-format track
1394 ubyte snaptype;
1396 TrackCache t; // used in read/write image
1397 char[0x200] name;
1398 char[0x200] dsc;
1400 public:
1401 @property const(char)[] namestr () const pure nothrow @trusted @nogc {
1402 foreach (immutable idx, char ch; name[]) if (ch == 0) return name[0..idx];
1403 return name[];
1406 @property const(char)[] dscstr () const pure nothrow @trusted @nogc {
1407 foreach (immutable idx, char ch; dsc[]) if (ch == 0) return dsc[0..idx];
1408 return dsc[];
1411 public:
1412 ubyte index; // INIT THIS!
1414 public:
1415 this (ubyte aindex) nothrow @trusted { index = aindex; clear(); }
1417 ~this () nothrow @trusted { clear(); }
1419 void clear () nothrow @trusted {
1420 import core.stdc.string : memset;
1422 if (rawdata !is null) {
1423 import core.stdc.stdlib : free;
1424 free(rawdata);
1425 rawdata = null;
1428 motor = 0;
1429 track = 0;
1431 rawsize = 0;
1432 cyls = 0;
1433 sides = 0;
1434 name[] = 0;
1435 dsc[] = 0;
1436 memset(trklen.ptr, 0, trklen.sizeof);
1437 memset(trkd.ptr, 0, trkd.sizeof);
1438 memset(trki.ptr, 0, trki.sizeof);
1439 memset(trkwp.ptr, 0, trkwp.sizeof);
1440 optype = 0;
1441 snaptype = 0;
1442 Config.WD93.writeProt(index, false);
1443 /*wd93comp.wd.trkcache.clear(); [vv]*/
1444 t.clear();
1447 protected void allocRawData (uint size) nothrow @trusted {
1448 import core.stdc.stdlib : realloc;
1449 if (size >= uint.max/8) assert(0, "invalid allocRawData call");
1450 if (rawsize != size) {
1451 rawdata = cast(ubyte*)realloc(rawdata, size+1024);
1452 if (rawdata is null) assert(0, "out of memory");
1453 rawsize = size;
1455 rawdata[0..rawsize] = 0;
1458 void newDisk (uint cyls, uint sides) nothrow @trusted {
1459 import core.stdc.stdlib : malloc;
1460 clear();
1461 this.cyls = cyls;
1462 this.sides = sides;
1463 uint len = MAX_TRACK_LEN;
1464 uint bitmap_len = uint(len+7U)>>3;
1465 uint len2 = len+bitmap_len*2;
1466 allocRawData(cyls*sides*len2);
1467 for (uint c = 0; c < cyls; c++) {
1468 for (uint h = 0; h < sides; h++) {
1469 trklen[c][h] = len;
1470 trkd[c][h] = rawdata+len2*(c*sides+h);
1471 trki[c][h] = trkd[c][h]+len;
1472 trkwp[c][h] = trkd[c][h]+len+bitmap_len;
1475 // wd93comp.wd.trkcache.clear(); // already done in free()
1478 // //////////////////////////////////////////////////////////////////// //
1479 // TRD/SCL/HoBeta READER
1480 enum TRDDiskType : ubyte {
1481 DS_80 = 0x16,
1482 DS_40 = 0x17,
1483 SS_80 = 0x18,
1484 SS_40 = 0x19,
1487 enum TRD_SIG = 0x10;
1489 align(1) static struct TRDDirEntryBase {
1490 align(1):
1491 char[8] name;
1492 ubyte type;
1493 ushort start;
1494 ushort length;
1495 ubyte seccnt; // Длина файла в секторах
1498 align(1) static struct TRDDirEntry {
1499 align(1):
1500 char[8] name;
1501 ubyte type;
1502 ushort start;
1503 ushort length;
1504 ubyte seccnt; // Длина файла в секторах
1506 ubyte sec; // Начальный сектор
1507 ubyte trk; // Начальная дорожка
1510 align(1) static struct TRDSec9 {
1511 align(1):
1512 ubyte Zero; // 00
1513 ubyte[224] Reserved;
1514 ubyte FirstFreeSec; // E1
1515 ubyte FirstFreeTrk; // E2
1516 ubyte DiskType; // E3
1517 ubyte FileCnt; // E4
1518 ushort FreeSecCnt; // E5, E6
1519 ubyte TrDosSig; // E7
1520 ubyte[2] Res1; // | 0
1521 ubyte[9] Res2; // | 32
1522 ubyte Res3; // | 0
1523 ubyte DelFileCnt; // F4
1524 char[8] Label; // F5-FC
1525 ubyte[3] Res4; // | 0
1528 align(1) static struct SCLHeader {
1529 align(1):
1530 char[8] Sig; // SINCLAIR
1531 ubyte FileCnt;
1532 TRDDirEntryBase[0] Files;
1535 // Проверка на то что диск имеет tr-dos формат
1536 bool isTRD () nothrow @trusted {
1537 static bool checkFiller (const(void)[] buf, char fill) nothrow @trusted @nogc {
1538 const(char)* p = cast(const(char)*)buf.ptr;
1539 foreach (immutable _; 0..buf.length) if (*p++ != fill) return false;
1540 return true;
1543 t.seek(this, 0, 0, SeekMode.LoadSectors);
1545 SectorHeader* hdr = t.getSector(9);
1546 if (hdr is null) return false;
1548 //conwriteln("000");
1549 if ((hdr.l&3) != 1) return false; // Проверка кода размера сектора (1 - 256 байт)
1551 TRDSec9* sec9 = cast(TRDSec9*)hdr.data;
1552 if (sec9 is null) return false;
1554 //conwriteln("001");
1555 if (sec9.Zero != 0) return false;
1557 //conwriteln("002");
1558 if (sec9.TrDosSig != TRD_SIG) return false;
1560 //conwriteln("003");
1561 if (!(checkFiller(sec9.Res2[], ' ') || checkFiller(sec9.Res2[], 0))) return false;
1563 //conwriteln("004");
1564 if (!(sec9.DiskType == TRDDiskType.DS_80 || sec9.DiskType == TRDDiskType.DS_40 ||
1565 sec9.DiskType == TRDDiskType.SS_80 || sec9.DiskType == TRDDiskType.SS_40)) return false;
1567 //conwriteln("005");
1568 return true;
1571 void formatTRD (uint CylCnt) nothrow @trusted {
1572 static immutable ubyte[16][3] lv =
1573 [ [ 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16 ],
1574 [ 1,9,2,10,3,11,4,12,5,13,6,14,7,15,8,16 ],
1575 [ 1,12,7,2,13,8,3,14,9,4,15,10,5,16,11,6 ] ];
1576 const(ubyte)* ilv;
1577 if (/*Config.WD93.trdInterleave < 0 ||*/ Config.WD93.trdInterleave >= lv.length) {
1578 ilv = lv[0].ptr;
1579 } else {
1580 ilv = lv[Config.WD93.trdInterleave].ptr;
1582 newDisk(CylCnt, 2);
1583 for (uint c = 0; c < cyls; c++) {
1584 for (uint side = 0; side < 2; side++) {
1585 t.seek(this, c, side, SeekMode.SeekOnly);
1586 t.s = 16;
1587 for (uint sn = 0; sn < 16; sn++) {
1588 uint s = ilv[sn]; //lv[Config.WD93.trdInterleave][sn];
1589 t.hdr[sn].n = cast(ubyte)s;
1590 t.hdr[sn].l = 1;
1591 t.hdr[sn].c = cast(ubyte)c;
1592 t.hdr[sn].s = 0;
1593 t.hdr[sn].c1 = t.hdr[sn].c2 = 0;
1594 t.hdr[sn].data = cast(ubyte*)1;
1596 t.format();
1601 void emptyDiskEmpty (uint FreeSecCnt=2544) nothrow @trusted {
1602 uint SecCnt = FreeSecCnt+16;
1603 uint CylCnt = SecCnt/(16*2)+(SecCnt%(16*2) ? 1 : 0);
1604 newDisk(CylCnt, 2);
1607 void emptyDiskTRD (uint FreeSecCnt=2544) nothrow @trusted {
1608 uint SecCnt = FreeSecCnt+16;
1609 uint CylCnt = SecCnt/(16*2)+(SecCnt%(16*2) ? 1 : 0);
1610 formatTRD(CylCnt);
1611 t.seek(this, 0, 0, SeekMode.LoadSectors);
1612 SectorHeader* sec9hdr = t.getSector(9);
1613 if (sec9hdr is null) return;
1614 TRDSec9* sec9 = cast(TRDSec9*)sec9hdr.data;
1615 sec9.FirstFreeTrk = 1; // first free track
1616 sec9.DiskType = TRDDiskType.DS_80; // 80T,DS
1617 sec9.FreeSecCnt = cast(ushort)FreeSecCnt; // free sec
1618 sec9.TrDosSig = TRD_SIG; // trdos flag
1619 sec9.Label[] = ' '; // label
1620 sec9.Res2[] = ' '; // reserved
1621 t.writeSector(9, sec9hdr.data); // update sector CRC
1624 bool addfile (const(ubyte)* hdr, const(ubyte)* data, bool tosys=false) nothrow @trusted {
1625 import core.stdc.string : memcpy;
1627 if (!isTRD) { conwriteln("not a TR-DOS disk"); return false; }
1629 t.seek(this, 0, 0, SeekMode.LoadSectors);
1630 SectorHeader* sec9hdr = t.getSector(9);
1631 assert(sec9hdr !is null);
1633 TRDSec9* sec9 = cast(TRDSec9*)sec9hdr.data;
1634 assert(sec9 !is null);
1636 if (sec9.FileCnt >= 128) { conwriteln("TRD: too many files"); return false; } // Каталог заполнен полностью
1638 uint len = (cast(TRDDirEntry*)hdr).seccnt;
1639 uint pos = sec9.FileCnt*TRDDirEntry.sizeof;
1640 SectorHeader* dir = t.getSector(1+pos/0x100);
1642 if (dir is null) { conwriteln("TRD: can't find directory"); return false; }
1644 if (tosys && len > 7*256) { conwriteln("TRD: file is too big for system area; using normal placement"); tosys = false; }
1645 if (!tosys && sec9.FreeSecCnt < len) { conwriteln("TRD: no room: len=", len, "; freesectors=", sec9.FreeSecCnt); return false; } // На диске нет места
1646 //conwriteln("TRD: room: len=", len, "; freesectors=", sec9.FreeSecCnt);
1648 TRDDirEntry* TrdDirEntry = cast(TRDDirEntry*)(dir.data+(pos&0xFF));
1649 memcpy(TrdDirEntry, hdr, 14);
1650 if (tosys) {
1651 TrdDirEntry.sec = 9;
1652 TrdDirEntry.trk = 0;
1653 } else {
1654 TrdDirEntry.sec = sec9.FirstFreeSec;
1655 TrdDirEntry.trk = sec9.FirstFreeTrk;
1658 t.writeSector(1+pos/0x100, dir.data);
1660 //pos = sec9.FirstFreeSec+16*sec9.FirstFreeTrk;
1661 pos = TrdDirEntry.sec+16*TrdDirEntry.trk;
1662 sec9.FirstFreeSec = (pos+len)&0x0F;
1663 sec9.FirstFreeTrk = ((pos+len)>>4)&0xff;
1664 sec9.FileCnt++;
1665 sec9.FreeSecCnt -= len;
1666 t.writeSector(9, sec9hdr.data);
1668 // goto next track. s8 become invalid
1669 for (uint i = 0; i < len; i++, pos++) {
1670 t.seek(this, pos/32, (pos/16)&1, SeekMode.LoadSectors);
1671 if (!t.trkd) { conwriteln("TRD: no dir track"); return false; }
1672 if (!t.writeSector((pos&0x0F)+1, data+i*0x100)) { conwriteln("TRD: can't write sector"); return false; }
1675 return true;
1678 // destroys wdsnapbuf - use after loading all files
1679 void addboot () nothrow @trusted {
1680 static immutable ubyte[273] bootsmall = [
1681 0x62,0x6f,0x6f,0x74,0x20,0x20,0x20,0x20,0x42,0xfc,0x00,0xfc,0x00,0x00,0x01,0xd8,
1682 0x73,0x00,0x01,0xf8,0x00,0xe7,0xbc,0xa7,0x3a,0xf9,0xc0,0xb0,0x22,0x32,0x33,0x39,
1683 0x31,0x30,0x22,0x3a,0xea,0x3a,0xf7,0x22,0xe8,0x0f,0xe8,0xbf,0xc0,0xc3,0xbb,0xca,
1684 0xbd,0xc8,0xca,0xb6,0xbc,0xbb,0xe8,0xde,0xd6,0xd6,0xd9,0x22,0xfd,0x36,0x53,0x0e,
1685 0xcd,0x6b,0x0d,0x09,0x01,0x05,0x09,0xe5,0xcd,0x13,0x3d,0x06,0x13,0x21,0x52,0x5d,
1686 0x96,0xd7,0x23,0x10,0xfb,0x3e,0x02,0xcd,0x01,0x16,0xe1,0x54,0x5d,0x01,0x08,0x00,
1687 0x35,0x28,0x28,0x34,0x28,0x2a,0xe5,0x09,0x7e,0xe1,0xfe,0x42,0x20,0x1d,0xe5,0x3e,
1688 0x20,0xd7,0x7e,0xd7,0xed,0xa0,0xea,0x9c,0x5d,0x21,0xd1,0x70,0x34,0x7e,0xd6,0x03,
1689 0x28,0x05,0x30,0xfa,0x3e,0x20,0xd7,0x3e,0x20,0xd7,0xe1,0x0e,0x10,0x09,0x18,0xcd,
1690 0x26,0x57,0x7e,0x0f,0xb6,0x77,0x2b,0x7c,0xb7,0x20,0xf7,0x48,0x21,0xf5,0x57,0x97,
1691 0x11,0x0b,0x00,0x06,0x03,0x19,0xb9,0x28,0x06,0x3c,0x10,0xf9,0x2b,0x18,0xf4,0x06,
1692 0x0a,0xed,0x5b,0xd3,0x70,0x22,0xd3,0x70,0x3e,0x0e,0x12,0x13,0x36,0x22,0x23,0x10,
1693 0xf9,0x21,0x08,0x5c,0x70,0x7e,0xfe,0x0d,0x28,0x30,0xfe,0x20,0xca,0x66,0x5d,0xcb,
1694 0xef,0xfe,0x70,0x28,0x1b,0xfe,0x6f,0x28,0x0a,0xfe,0x61,0x28,0x11,0xfe,0x71,0x20,
1695 0xe4,0x0d,0x0d,0x0d,0xf2,0xc6,0x5d,0xed,0x4b,0xd1,0x70,0x0d,0x18,0xae,0x0c,0x0c,
1696 0x0c,0x3a,0xd1,0x70,0x3d,0x91,0x30,0xa4,0x18,0xa1,0x60,0x69,0x29,0x29,0x29,0x11,
1697 0x01,0x68,0x19,0x11,0x52,0x5d,0x0e,0x08,0xed,0xb0,0xc3,0x03,0x3d,0x80,0xaa,0x01,
1698 0x00,
1699 /* kempstonized
1700 0x62,0x6F,0x6F,0x74,0x20,0x20,0x20,0x20,0x42,0xFC,0x00,0xFC,0x00,0x00,0x01,0xD8,
1701 0x73,0x00,0x01,0xF8,0x00,0xE7,0xBC,0xA7,0x3A,0xF9,0xC0,0xB0,0x22,0x32,0x33,0x39,
1702 0x30,0x38,0x22,0x3A,0xEA,0x3A,0xF7,0x22,0x17,0x09,0x20,0x50,0x4F,0x4C,0x54,0x45,
1703 0x52,0x47,0x45,0x59,0x53,0x54,0x2B,0x4B,0x38,0x22,0xFD,0x36,0x53,0x0E,0xCD,0x6B,
1704 0x0D,0x09,0x01,0x05,0x09,0xE5,0xCD,0x13,0x3D,0x11,0x52,0x5D,0x0E,0x11,0xCD,0x3C,
1705 0x20,0x3E,0x02,0xCD,0x01,0x16,0xE1,0x54,0x5D,0x01,0x08,0x00,0x35,0x28,0x28,0x34,
1706 0x28,0x2A,0xE5,0x09,0x7E,0xE1,0xFE,0x42,0x20,0x1D,0xE5,0x3E,0x20,0xD7,0x7E,0xD7,
1707 0xED,0xA0,0xEA,0x98,0x5D,0x21,0xD1,0x70,0x34,0x7E,0xD6,0x03,0x28,0x05,0x30,0xFA,
1708 0x3E,0x20,0xD7,0x3E,0x20,0xD7,0xE1,0x0E,0x10,0x09,0x18,0xCD,0x26,0x57,0x7E,0x0F,
1709 0xB6,0x77,0x2B,0xCB,0x74,0x20,0xF7,0x48,0x21,0xF5,0x57,0xAF,0x11,0x0B,0x00,0x06,
1710 0x03,0x19,0xB9,0x28,0x06,0x3C,0x10,0xF9,0x2B,0x18,0xF4,0x06,0x0A,0xED,0x5B,0xD3,
1711 0x70,0x22,0xD3,0x70,0x3E,0x0E,0x12,0x13,0x36,0x22,0x23,0x10,0xF9,0x21,0x08,0x5C,
1712 0x70,0x06,0x05,0x76,0x10,0xFD,0x7E,0xFE,0x20,0xCA,0x64,0x5D,0xAF,0xDB,0x1F,0x1F,
1713 0x38,0x2E,0x1F,0x38,0x1E,0x1F,0x38,0x26,0x1F,0x38,0x16,0x1F,0x30,0xE3,0x60,0x69,
1714 0x29,0x29,0x29,0x11,0x01,0x68,0x19,0x11,0x52,0x5D,0x0E,0x08,0xED,0xB0,0xC3,0x03,
1715 0x3D,0x0D,0x0D,0x0D,0xF2,0xC2,0x5D,0xED,0x4B,0xD1,0x70,0x0D,0x18,0x9A,0x0C,0x0C,
1716 0x0C,0x3A,0xD1,0x70,0x3D,0x91,0x30,0x90,0x18,0x8D,0x00,0x00,0x00,0x80,0xAA,0x01,
1717 0x00,
1721 conwriteln("addboot: checking disk...");
1722 if (!isTRD) { conwriteln("not a TR-DOS disk"); return; }
1724 t.seek(this, 0, 0, SeekMode.LoadSectors);
1726 SectorHeader* hdr = t.getSector(9);
1727 assert(hdr !is null);
1729 TRDSec9* sec9 = cast(TRDSec9*)hdr.data;
1730 assert(sec9 !is null);
1732 for (uint s = 0; s < 8; s++) {
1733 SectorHeader* sc = t.getSector(1+s);
1734 if (sc is null) return;
1735 TRDDirEntry* TrdDirEntry = cast(TRDDirEntry*)sc.data;
1736 for (uint i = 0; i < 16; i++) {
1737 import core.stdc.string : memcmp;
1738 if (memcmp(TrdDirEntry[i].name.ptr, "boot B".ptr, 9) == 0) return;
1742 if (defaultBootHob.length) {
1743 conwriteln("addboot: adding boot...");
1744 wdsnapbuf[0..defaultBootHob.length] = defaultBootHob[];
1745 wdsnapsize = cast(uint)defaultBootHob.length;
1746 } else {
1747 conwriteln("addboot: adding built-in boot...");
1748 wdsnapbuf[0..bootsmall.length] = bootsmall[];
1749 wdsnapsize = cast(uint)bootsmall.length;
1752 readHOB(Config.WD93.trdAddBootToSys);
1755 bool readSCL () nothrow @trusted {
1756 uint size = 0, i;
1757 SCLHeader* SclHdr = cast(SCLHeader*)wdsnapbuf;
1758 for (i = 0; i < SclHdr.FileCnt; i++) size += SclHdr.Files.ptr[i].seccnt;
1759 conwriteln("SCL size: ", size, " sectors");
1760 if (size < 2544) size = 2544; else if (size > 2635) size = 2635;
1761 //if (size > 2544) size = 2544;
1762 //size = 2544; // so we'll have room for boot, for example
1763 emptyDiskTRD(size);
1764 ubyte* data = wdsnapbuf.ptr+SCLHeader.sizeof+SclHdr.FileCnt*TRDDirEntryBase.sizeof;
1765 for (i = 0; i < SclHdr.FileCnt; i++) {
1766 if (!addfile(cast(ubyte*)&SclHdr.Files.ptr[i], data)) {
1767 conwriteln("can't add file #", i, "...");
1768 return false;
1770 data += SclHdr.Files.ptr[i].seccnt*0x100;
1772 if (Config.WD93.trdAddBoot) addboot();
1773 return true;
1776 bool readHOB (bool tosys=false) nothrow @trusted {
1777 if (rawdata is null) emptyDiskTRD(2544);
1778 wdsnapbuf[13] = wdsnapbuf[14]; // copy length
1779 return addfile(wdsnapbuf.ptr, wdsnapbuf.ptr+0x11, tosys);
1782 bool readTRD () nothrow @trusted {
1783 uint CylCnt = wdsnapsize/(256*16*2)+(wdsnapsize%(256*16*2) ? 1 : 0);
1784 if (CylCnt > MAX_CYLS) {
1785 conwritefln!"cylinders (%d) > MAX_CYLS(%d)"(CylCnt, MAX_CYLS);
1786 return false;
1788 formatTRD(CylCnt);
1789 for (uint i = 0; i < wdsnapsize; i += 0x100) {
1790 t.seek(this, i>>13, (i>>12)&1, SeekMode.LoadSectors);
1791 t.writeSector(((i>>8)&0x0F)+1, wdsnapbuf.ptr+i);
1793 if (Config.WD93.trdAddBoot) addboot();
1794 return true;
1797 void writeTRD (VFile fo) {
1798 static immutable ubyte[256] zerosec = 0;
1799 for (uint i = 0; i < cyls*sides*16; i++) {
1800 t.seek(this, i>>5, (i>>4)&1, SeekMode.LoadSectors);
1801 SectorHeader* hdr = t.getSector((i&0x0F)+1);
1802 const(ubyte)* ptr = zerosec.ptr;
1803 if (hdr && hdr.data) ptr = hdr.data;
1804 fo.rawWriteExact(ptr[0..256]);
1808 // will destroy wdsnapbuf
1809 void writeSCL (VFile fo) {
1810 if (!isTRD) { conwriteln("the disk doesn't look like TR-DOS one. T_T"); throw new Exception("SCL error"); }
1812 t.seek(this, 0, 0, SeekMode.LoadSectors);
1813 SectorHeader* sec9hdr = t.getSector(9);
1814 assert(sec9hdr !is null);
1816 TRDSec9* sec9 = cast(TRDSec9*)sec9hdr.data;
1817 assert(sec9 !is null);
1819 if (sec9.FileCnt > 128) { conwriteln("TRD: too many files"); throw new Exception("SCL error"); } // Каталог заполнен полностью
1821 uint wdsbpos = 0;
1823 void put (const(void)[] data) {
1824 import core.stdc.string : memcpy;
1825 if (wdsbpos+data.length > wdsnapbuf.length) { conwriteln("wtf?!"); throw new Exception("SCL error"); }
1826 memcpy(wdsnapbuf.ptr+wdsbpos, data.ptr, data.length);
1827 wdsbpos += cast(uint)data.length;
1830 void putB (ubyte b) { put((&b)[0..1]); }
1831 //void putW (ushort w) { put(w&0xff); put(w>>8); }
1833 put("SINCLAIR");
1834 putB(0); // file count, will be fixed later
1836 // write headers
1837 foreach (immutable uint fidx; 0..sec9.FileCnt) {
1838 SectorHeader* dir = t.getSector(1+fidx/16);
1839 if (dir is null) throw new Exception("SCL error");
1840 auto th = (cast(TRDDirEntry*)dir.data)+fidx%16;
1841 if (th.name[0] == 0) continue;
1842 // increment file count
1843 ++wdsnapbuf[8];
1844 //conwriteln("files: ", wdsnapbuf[8], " (", fidx, ")");
1845 put((cast(ubyte*)th)[0..14]); // header
1848 // write data
1849 foreach (immutable uint fidx; 0..sec9.FileCnt) {
1850 t.seek(this, 0, 0, SeekMode.LoadSectors);
1851 SectorHeader* dir = t.getSector(1+fidx/16);
1852 if (dir is null) throw new Exception("SCL error");
1853 auto th = (cast(TRDDirEntry*)dir.data)+fidx%16;
1854 if (th.name[0] == 0) continue;
1855 auto size = th.seccnt;
1856 ubyte sec = th.sec;
1857 ubyte trk = th.trk;
1858 //conwriteln("file idx: ", fidx, "; sector: ", th.sec, "; track: ", th.trk, "; size: ", size);
1859 while (size-- > 0) {
1860 t.seek(this, trk/2, trk&1, SeekMode.LoadSectors);
1861 dir = t.getSector(1+sec);
1862 if (dir is null) throw new Exception("SCL error");
1863 put((cast(ubyte*)dir.data)[0..256]);
1864 if (++sec == 16) { sec = 0; ++trk; }
1867 //conwriteln("done");
1869 // calculate checksum
1870 uint csum = 0;
1871 foreach (immutable ubyte b; wdsnapbuf[0..wdsbpos]) csum += b;
1872 putB(csum&0xff);
1873 putB((csum>>8)&0xff);
1874 putB((csum>>16)&0xff);
1875 putB((csum>>24)&0xff);
1877 fo.rawWriteExact(wdsnapbuf[0..wdsbpos]);
1880 // //////////////////////////////////////////////////////////////////// //
1881 // UDI READER
1882 static align(1) struct UDI {
1883 align(1):
1884 char[3] label;
1885 ubyte cyls;
1886 ubyte sides;
1889 // http://speccy.info/UDI
1890 bool readUDI () nothrow @trusted {
1891 if (wdsnapsize < 16) return false;
1892 if ((cast(const(char)[])wdsnapbuf)[0..4] != "UDI!") return false;
1893 if (wdsnapbuf.ptr[8] != 0) return false; // invalid version
1894 if (wdsnapbuf.ptr[9] == 0 || wdsnapbuf.ptr[9] > MAX_CYLS) return false;
1895 if (wdsnapbuf.ptr[10] > 2) return false; // sides
1896 //if (wdsnapbuf.ptr[11] != 0) return false; // reserved
1897 // extra data
1898 if (wdsnapbuf.ptr[12] != 0 || wdsnapbuf.ptr[13] != 0) return false;
1899 if (wdsnapbuf.ptr[14] != 0 || wdsnapbuf.ptr[15] != 0) return false;
1901 clear();
1902 uint c, s;
1903 uint mem = 0;
1904 const(ubyte)* ptr = wdsnapbuf.ptr+0x10;
1906 for (c = 0; c <= wdsnapbuf[9]; c++) {
1907 for (s = 0; s <= wdsnapbuf[10]; s++) {
1908 if (*ptr) return false;
1909 uint sz = *cast(ushort*)(ptr+1);
1910 sz += sz/8+(sz&7 ? 1 : 0);
1911 mem += sz;
1912 ptr += 3+sz;
1913 if (ptr > wdsnapbuf.ptr+wdsnapsize) return false;
1917 cyls = wdsnapbuf[9]+1;
1918 sides = wdsnapbuf[10]+1;
1920 if (cyls > MAX_CYLS) {
1921 conwritefln!"cylinders (%d) > MAX_CYLS(%d)"(cyls, MAX_CYLS);
1922 return false;
1925 if (sides > 2) {
1926 conwritefln!"sides (%d) > 2"(sides);
1927 return false;
1931 rawsize = align_by(mem, 4096);
1932 rawdata = (unsigned char*)VirtualAlloc(0, rawsize, MEM_COMMIT, PAGE_READWRITE);
1934 allocRawData(mem);
1935 ptr = wdsnapbuf.ptr+0x10;
1936 ubyte* dst = rawdata;
1938 for (c = 0; c < cyls; c++) {
1939 for (s = 0; s < sides; s++) {
1940 import core.stdc.string : memcpy;
1941 uint sz = *cast(ushort*)(ptr+1);
1942 trklen[c][s] = sz;
1943 trkd[c][s] = dst;
1944 trki[c][s] = dst+sz;
1945 sz += sz/8+(sz&7 ? 1 : 0);
1946 memcpy(dst, ptr+3, sz);
1947 ptr += 3+sz;
1948 dst += sz;
1951 uint crc1 = *(cast(uint*)ptr);
1952 int crc2 = -1;
1953 udi_buggy_crc(crc2, wdsnapbuf.ptr, cast(uint)(ptr-wdsnapbuf.ptr));
1954 if (crc1 != crc2) {
1955 conwritefln!"udi crc mismatch: image=0x%08X, calc=0x%08X"(crc1, crc2);
1958 if (wdsnapbuf[11]&1) {
1959 dsc[] = 0;
1960 foreach (ref char dch; dsc[]) {
1961 if (*ptr == 0) break;
1962 dch = cast(char)*ptr;
1963 ++ptr;
1967 if (Config.WD93.trdAddBoot) addboot();
1968 return true;
1971 void writeUDI (VFile fo) {
1972 import core.stdc.string : memcpy, memset;
1974 wdsnapbuf[] = 0;
1975 wdsnapbuf[0] = 'U';
1976 wdsnapbuf[1] = 'D';
1977 wdsnapbuf[2] = 'I';
1978 wdsnapbuf[3] = '!';
1979 wdsnapbuf[8] = wdsnapbuf[11] = 0;
1980 wdsnapbuf[9] = cast(ubyte)(cyls-1);
1981 wdsnapbuf[10] = cast(ubyte)(sides-1);
1982 //*(unsigned*)(wdsnapbuf+12) = 0;
1984 ubyte* dst = wdsnapbuf.ptr+0x10;
1985 for (uint c = 0; c < cyls; c++) {
1986 for (uint s = 0; s < sides; s++) {
1987 *dst++ = 0;
1988 uint len = trklen[c][s];
1989 *cast(ushort*)dst = cast(ushort)len;
1990 dst += 2;
1991 memcpy(dst, trkd[c][s], len);
1992 dst += len;
1993 len = (len+7)/8;
1994 memcpy(dst, trki[c][s], len);
1995 dst += len;
1998 if (dscstr.length) {
1999 //strcpy((char*)dst, dsc), dst += strlen(dsc)+1, wdsnapbuf[11] = 1;
2000 memcpy(dst, dscstr.ptr, dscstr.length);
2001 dst += dscstr.length;
2002 *dst++ = 0;
2003 wdsnapbuf[11] = 1;
2005 *cast(uint*)(wdsnapbuf.ptr+4) = cast(uint)(dst-wdsnapbuf.ptr);
2006 int crc = -1;
2007 udi_buggy_crc(crc, wdsnapbuf.ptr, cast(uint)(dst-wdsnapbuf.ptr));
2008 *cast(uint*)dst = crc;
2009 dst += 4;
2010 fo.rawWriteExact(wdsnapbuf[0..cast(uint)(dst-wdsnapbuf.ptr)]);
2013 // //////////////////////////////////////////////////////////////////// //
2014 // ISD READER
2015 void formatISD () nothrow @trusted {
2016 static immutable ubyte[5] sn = [ 1, 2, 3, 4, 9 ];
2017 newDisk(80, 2);
2018 for (uint c = 0; c < cyls; c++) {
2019 for (uint h = 0; h < 2; h++) {
2020 t.seek(this, c, h, SeekMode.SeekOnly);
2021 t.s = 5;
2022 for (uint s = 0; s < 5; s++) {
2023 uint n = sn[s];
2024 t.hdr[s].n = cast(ubyte)n;
2025 t.hdr[s].l = 3;
2026 t.hdr[s].c = cast(ubyte)c;
2027 t.hdr[s].s = 0;
2028 t.hdr[s].c1 = t.hdr[s].c2 = 0;
2029 t.hdr[s].data = cast(ubyte*)1;
2031 t.format();
2036 bool readISD () nothrow @trusted {
2037 static immutable ubyte[5] sn = [ 1, 2, 3, 4, 9 ];
2038 formatISD();
2039 for (uint c = 0; c < cyls; c++) {
2040 for (uint h = 0; h < 2; h++) {
2041 for (uint s = 0; s < 5; s++) {
2042 t.seek(this, c, h, SeekMode.LoadSectors);
2043 t.writeSector(sn[s], wdsnapbuf.ptr+(c*10+h*5+s)*1024);
2047 return true;
2050 void writeISD (VFile fo) {
2051 for (uint c = 0; c < 80; c++) {
2052 for (uint h = 0; h < 2; h++) {
2053 t.seek(this, c, h, SeekMode.LoadSectors);
2054 for (uint s = 0; s < 5; s++) {
2055 fo.rawWriteExact(t.hdr[s].data[0..1024]);
2061 // //////////////////////////////////////////////////////////////////// //
2062 // FDI
2063 align(1) static struct TFdiSecHdr {
2064 align(1):
2065 enum {
2066 FL_DELETED_DATA = 0x80,
2067 FL_NO_DATA = 0x40,
2068 FL_GOOD_CRC_4096 = 0x20,
2069 FL_GOOD_CRC_2048 = 0x10,
2070 FL_GOOD_CRC_1024 = 0x8,
2071 FL_GOOD_CRC_512 = 0x4,
2072 FL_GOOD_CRC_256 = 0x2,
2073 FL_GOOD_CRC_128 = 0x1,
2076 ubyte c;
2077 ubyte h;
2078 ubyte r;
2079 ubyte n;
2080 // flags:
2081 // bit 7 - 1 = deleted data (F8) / 0 = normal data (FB)
2082 // bit 6 - 1 - sector with no data
2083 // bits 0..5 - 1 = good crc for sector size (128, 256, 512, 1024, 2048, 4096)
2084 ubyte fl;
2085 ushort DataOffset;
2088 align(1) static struct TFdiTrkHdr {
2089 align(1):
2090 uint TrkOffset;
2091 ushort Res1;
2092 ubyte Spt;
2093 TFdiSecHdr[0] Sec;
2096 align(1) static struct TFdiHdr {
2097 align(1):
2098 char[3] Sig;
2099 ubyte Rw;
2100 ushort c;
2101 ushort h;
2102 ushort TextOffset;
2103 ushort DataOffset;
2104 ushort AddLen;
2105 ubyte[0] AddData; // AddLen -> TFdiAddInfo
2106 //TFdiTrkHdr Trk[c*h];
2109 align(1) static struct TFdiAddInfo {
2110 align(1):
2111 enum BAD_BYTES = 1;
2112 enum FDI_2 = 2;
2113 ushort Ver; // 2 - FDI 2
2114 ushort AddInfoType; // 1 - bad bytes info
2115 uint TrkAddInfoOffset; // -> TFdiTrkAddInfo
2116 uint DataOffset;
2119 align(1) static struct TFdiSecAddInfo {
2120 align(1):
2121 ubyte Flags; // 1 - Массив сбойных байтов присутствует
2122 // Смещение битового массива сбойных байтов внутри трэка
2123 // Число битов определяется размером сектора
2124 // Один бит соответствует одному сбойному байту
2125 ushort DataOffset;
2128 align(1) static struct TFdiTrkAddInfo {
2129 align(1):
2130 uint TrkOffset; // Смещение массива сбойных байтов для трэка относительно TFdiAddInfo->DataOffset,
2131 // 0xFFFFFFFF - Массив описателей параметров секторов отсутствует
2132 TFdiSecAddInfo[0] Sec; // Spt
2135 // http://speccy.info/FDI
2136 bool readFDI () nothrow @trusted {
2137 if (wdsnapsize < 16) return false;
2138 if ((cast(const(char)[])wdsnapbuf)[0..3] != "FDI") return false;
2139 if (wdsnapbuf.ptr[5] != 0 || wdsnapbuf.ptr[4] == 0 || wdsnapbuf.ptr[4] > MAX_CYLS) return false;
2140 if (wdsnapbuf.ptr[7] != 0 || wdsnapbuf.ptr[6] < 1 || wdsnapbuf.ptr[6] > 2) return false; // heads
2142 const(TFdiHdr)* FdiHdr = cast(const(TFdiHdr)*)wdsnapbuf.ptr;
2143 uint cyls = FdiHdr.c;
2144 uint sides = FdiHdr.h;
2145 uint AddLen = FdiHdr.AddLen;
2147 if (cyls > MAX_CYLS) {
2148 conwritefln!"cylinders (%d) > MAX_CYLS(%d)"(cyls, MAX_CYLS);
2149 return false;
2152 if (sides > 2) {
2153 conwritefln!"sides (%d) > 2"(sides);
2154 return false;
2157 newDisk(cyls, sides);
2159 const(TFdiAddInfo)* FdiAddInfo = null;
2160 const(TFdiTrkAddInfo)* FdiTrkAddInfo = null;
2161 if (AddLen >= TFdiAddInfo.sizeof) {
2162 // Проверить параметры FdiAddInfo (версию, тип и т.д.)
2163 FdiAddInfo = cast(const(TFdiAddInfo)*)FdiHdr.AddData.ptr;
2164 if (FdiAddInfo.Ver >= TFdiAddInfo.FDI_2 && FdiAddInfo.AddInfoType == TFdiAddInfo.BAD_BYTES) {
2165 FdiTrkAddInfo = cast(const(TFdiTrkAddInfo)*)(wdsnapbuf.ptr+FdiAddInfo.TrkAddInfoOffset);
2169 //strncpy(dsc, (const char *)&wdsnapbuf[FdiHdr.TextOffset], sizeof(dsc));
2170 //dsc[sizeof(dsc) - 1] = 0;
2171 dsc[] = 0;
2172 foreach (immutable idx, ref char dch; dsc[]) {
2173 if (wdsnapbuf[FdiHdr.TextOffset+idx] == 0) break;
2174 dch = cast(char)(wdsnapbuf[FdiHdr.TextOffset+idx]);
2177 const(TFdiTrkHdr)* FdiTrkHdr = cast(const(TFdiTrkHdr)*)&wdsnapbuf[TFdiHdr.sizeof+FdiHdr.AddLen];
2178 ubyte* dat = wdsnapbuf.ptr+FdiHdr.DataOffset;
2180 for (uint c = 0; c < cyls; c++) {
2181 for (uint s = 0; s < sides; s++) {
2182 t.seek(this, c,s, SeekMode.SeekOnly);
2184 ubyte *t0 = dat+FdiTrkHdr.TrkOffset;
2185 uint ns = FdiTrkHdr.Spt;
2186 ubyte *wp0 = null;
2188 if (FdiTrkAddInfo && FdiTrkAddInfo.TrkOffset != uint.max) {
2189 wp0 = wdsnapbuf.ptr+FdiAddInfo.DataOffset+FdiTrkAddInfo.TrkOffset;
2190 if (wp0 > wdsnapbuf.ptr+wdsnapsize) {
2191 conwriteln("bad bytes data is beyond disk image end");
2192 return false;
2196 for (uint sec = 0; sec < ns; sec++) {
2197 t.hdr[sec].c = FdiTrkHdr.Sec.ptr[sec].c;
2198 t.hdr[sec].s = FdiTrkHdr.Sec.ptr[sec].h;
2199 t.hdr[sec].n = FdiTrkHdr.Sec.ptr[sec].r;
2200 t.hdr[sec].l = FdiTrkHdr.Sec.ptr[sec].n;
2201 t.hdr[sec].c1 = 0;
2202 t.hdr[sec].wp = null;
2204 if (FdiTrkHdr.Sec.ptr[sec].fl&TFdiSecHdr.FL_NO_DATA) {
2205 t.hdr[sec].data = null;
2206 } else {
2207 if (t0+FdiTrkHdr.Sec.ptr[sec].DataOffset > wdsnapbuf.ptr+wdsnapsize) {
2208 conwriteln("sector data is beyond disk image end");
2209 return false;
2211 t.hdr[sec].data = t0+FdiTrkHdr.Sec.ptr[sec].DataOffset;
2213 if (FdiTrkAddInfo && FdiTrkAddInfo.TrkOffset != uint.max) {
2214 t.hdr[sec].wp = (FdiTrkAddInfo.Sec.ptr[sec].Flags&1 ? (wp0+FdiTrkAddInfo.Sec.ptr[sec].DataOffset) : null);
2217 #if 0
2218 if(FdiTrkHdr.Sec.ptr[sec].n > 3)
2220 ubyte buf[1+1024];
2221 buf[0] = (FdiTrkHdr.Sec.ptr[sec].fl&TFdiSecHdr::FL_DELETED_DATA ? 0xF8 : 0xFB);
2222 memcpy(buf+1, t.hdr[sec].data, 128U<<(FdiTrkHdr.Sec.ptr[sec].n&3));
2224 ushort crc_calc = wd93_crc(buf, (128U<<(FdiTrkHdr.Sec.ptr[sec].n&3))+1);
2225 ushort crc_from_hdr = *(ushort*)(t.hdr[sec].data+(128U<<(FdiTrkHdr.Sec.ptr[sec].n&3)));
2226 printf("phys: c=%-2u, h=%u, s=%u|hdr: c=0x%02X, h=0x%02X, r=0x%02X, n=%02X(%u)|crc1=0x%04X, crc2=0x%04X\n",
2227 c, s, sec,
2228 FdiTrkHdr.Sec.ptr[sec].c, FdiTrkHdr.Sec.ptr[sec].h, FdiTrkHdr.Sec.ptr[sec].r, FdiTrkHdr.Sec.ptr[sec].n, (FdiTrkHdr.Sec.ptr[sec].n&3),
2229 crc_calc, crc_from_hdr);
2231 if(crc_calc == crc_from_hdr)
2233 TFdiTrkHdr *FdiTrkHdrRW = const_cast<TFdiTrkHdr *>(FdiTrkHdr);
2234 FdiTrkHdrRW.Sec.ptr[sec].fl |= (1<<(FdiTrkHdr.Sec.ptr[sec].n&3));
2238 #endif
2240 t.hdr[sec].c2 = (FdiTrkHdr.Sec.ptr[sec].fl&(1<<(FdiTrkHdr.Sec.ptr[sec].n&3)) ? 0 : 2); // [vv]
2242 } // sec
2243 t.s = ns;
2244 t.format();
2246 if (FdiTrkAddInfo) {
2247 FdiTrkAddInfo = cast(const(TFdiTrkAddInfo)*)((cast(const(ubyte)*)FdiTrkAddInfo)+TFdiTrkAddInfo.sizeof+
2248 (FdiTrkAddInfo.TrkOffset != uint.max ? FdiTrkHdr.Spt*TFdiSecAddInfo.sizeof : 0));
2251 FdiTrkHdr = cast(const(TFdiTrkHdr)*)((cast(const(ubyte)*)FdiTrkHdr)+TFdiTrkHdr.sizeof+FdiTrkHdr.Spt*TFdiSecHdr.sizeof);
2252 } // s
2254 if (Config.WD93.trdAddBoot) addboot();
2255 return true;
2258 void writeFDI (VFile fo) {
2259 auto fspos = fo.tell;
2261 uint b, c, s, se, total_s = 0;
2262 uint sectors_wp = 0; // Общее число секторов для которых пишутся заголовки с дополнительной информацией
2263 uint total_size = 0; // Общий размер данных занимаемый секторами
2265 // Подсчет общего числа секторов на диске
2266 for (c = 0; c < cyls; c++) {
2267 for (s = 0; s < sides; s++) {
2268 t.seek(this, c, s, SeekMode.LoadSectors);
2269 for (se = 0; se < t.s; se++) {
2270 total_size += (t.hdr[se].data ? t.hdr[se].datlen : 0);
2272 for (se = 0; se < t.s; se++) {
2273 if (t.hdr[se].wp_start) {
2274 sectors_wp += t.s;
2275 break;
2278 total_s += t.s;
2282 uint AddLen = (sectors_wp ? cast(uint)TFdiAddInfo.sizeof : 0);
2283 uint tlen = cast(uint)dscstr.length+1;
2284 uint hsize = cast(uint)(TFdiHdr.sizeof+AddLen+cyls*sides*TFdiTrkHdr.sizeof+total_s*TFdiSecHdr.sizeof);
2285 uint AddHdrsSize = cast(uint)(cyls*sides*TFdiTrkAddInfo.sizeof+sectors_wp*TFdiSecAddInfo.sizeof);
2287 // Формирование FDI заголовка
2288 TFdiHdr* FdiHdr = cast(TFdiHdr*)wdsnapbuf;
2289 FdiHdr.Sig = "FDI";
2290 FdiHdr.Rw = 0;
2291 FdiHdr.c = cast(ushort)cyls;
2292 FdiHdr.h = cast(ushort)sides;
2293 FdiHdr.TextOffset = cast(ushort)hsize;
2294 FdiHdr.DataOffset = cast(ushort)(FdiHdr.TextOffset+tlen);
2295 FdiHdr.AddLen = cast(ushort)AddLen;
2297 TFdiAddInfo* FdiAddInfo = cast(TFdiAddInfo*)FdiHdr.AddData.ptr;
2298 if (AddLen) {
2299 FdiAddInfo.Ver = TFdiAddInfo.FDI_2; // FDI ver 2
2300 FdiAddInfo.AddInfoType = TFdiAddInfo.BAD_BYTES; // Информация о сбойных байтах
2301 FdiAddInfo.TrkAddInfoOffset = FdiHdr.DataOffset+total_size;
2302 FdiAddInfo.DataOffset = FdiAddInfo.TrkAddInfoOffset+AddHdrsSize;
2305 // Запись FDI заголовка с дополнительными данными
2306 fo.rawWriteExact((cast(ubyte*)FdiHdr)[0..TFdiHdr.sizeof+AddLen]);
2308 uint trkoffs = 0;
2309 for (c = 0; c < cyls; c++) {
2310 for (s = 0; s < sides; s++) {
2311 t.seek(this, c, s, SeekMode.LoadSectors);
2313 // Формирование заголовка трэка
2314 TFdiTrkHdr FdiTrkHdr;
2315 FdiTrkHdr.TrkOffset = trkoffs;
2316 FdiTrkHdr.Res1 = 0;
2317 FdiTrkHdr.Spt = cast(ubyte)t.s;
2319 // Запись заголовка трэка
2320 fo.rawWriteExact((cast(ubyte*)&FdiTrkHdr)[0..FdiTrkHdr.sizeof]);
2322 uint secoffs = 0;
2323 for (se = 0; se < t.s; se++) {
2324 // Формирование заголовка сектора
2325 TFdiSecHdr FdiSecHdr;
2326 FdiSecHdr.c = t.hdr[se].c;
2327 FdiSecHdr.h = t.hdr[se].s;
2328 FdiSecHdr.r = t.hdr[se].n;
2329 FdiSecHdr.n = t.hdr[se].l;
2330 FdiSecHdr.fl = 0;
2332 if (t.hdr[se].data) {
2333 if (t.hdr[se].data[-1] == 0xF8)
2334 FdiSecHdr.fl |= TFdiSecHdr.FL_DELETED_DATA;
2335 else
2336 FdiSecHdr.fl |= (t.hdr[se].c2 ? (1<<(t.hdr[se].l&3)) : 0); // [vv]
2337 } else {
2338 FdiSecHdr.fl |= TFdiSecHdr.FL_NO_DATA;
2341 FdiSecHdr.DataOffset = cast(ushort)secoffs;
2343 // Запись заголовка сектора
2344 fo.rawWriteExact((cast(ubyte*)&FdiSecHdr)[0..FdiSecHdr.sizeof]);
2345 secoffs += t.hdr[se].datlen;
2347 trkoffs += secoffs;
2351 // Запись комментария
2352 auto cfpos = fo.tell;
2353 if (cfpos > fspos+FdiHdr.TextOffset) assert(0, "FDI writer internal error");
2354 while (cfpos < fspos+FdiHdr.TextOffset) {
2355 fo.writeNum!ubyte(0);
2356 ++cfpos;
2358 //fo.seek(fspos+FdiHdr.TextOffset);
2359 if (tlen > 1) fo.rawWriteExact(dsc[0..tlen]);
2360 fo.writeNum!ubyte(0);
2362 // Запись зон данных трэков
2363 for (c = 0; c < cyls; c++) {
2364 for (s = 0; s < sides; s++) {
2365 t.seek(this, c, s, SeekMode.LoadSectors);
2366 for (uint sex = 0; sex < t.s; sex++) {
2367 if (t.hdr[sex].data) {
2368 fo.rawWriteExact((cast(ubyte*)t.hdr[sex].data)[0..t.hdr[sex].datlen]);
2374 // Запись дополниетльной информации (информации о сбойных байтах)
2375 if (AddLen) {
2376 trkoffs = 0;
2377 for (c = 0; c < cyls; c++) {
2378 for (s = 0; s < sides; s++) {
2379 t.seek(this, c, s, SeekMode.LoadSectors);
2381 // Формирование заголовка трэка
2382 TFdiTrkAddInfo FdiTrkAddInfo;
2383 FdiTrkAddInfo.TrkOffset = uint.max;
2384 for (b = 0; b < t.trklen; b++) {
2385 if (t.getWriteProtected(b)) {
2386 FdiTrkAddInfo.TrkOffset = trkoffs;
2387 break;
2391 // Запись заголовка трэка
2392 fo.rawWriteExact((cast(ubyte*)&FdiTrkAddInfo)[0..FdiTrkAddInfo.sizeof]);
2394 uint secoffs = 0;
2395 if (FdiTrkAddInfo.TrkOffset != uint.max) {
2396 for (se = 0; se < t.s; se++) {
2397 // Формирование заголовка сектора
2398 TFdiSecAddInfo FdiSecAddInfo;
2399 FdiSecAddInfo.Flags = 0;
2400 FdiSecAddInfo.DataOffset = 0;
2402 if (t.hdr[se].wp_start) {
2403 FdiSecAddInfo.Flags |= 1;
2404 FdiSecAddInfo.DataOffset = cast(ushort)secoffs;
2407 // Запись заголовка сектора
2408 fo.rawWriteExact((cast(ubyte*)&FdiSecAddInfo)[0..FdiSecAddInfo.sizeof]);
2409 secoffs += (t.hdr[se].wp_start ? ((t.hdr[se].datlen+7)>>3) : 0);
2412 trkoffs += secoffs;
2416 // Запись зон сбойных байтов
2417 for (c = 0; c < cyls; c++) {
2418 for (s = 0; s < sides; s++) {
2419 t.seek(this, c, s, SeekMode.LoadSectors);
2420 for (uint sex = 0; sex < t.s; sex++) {
2421 if (t.hdr[sex].wp_start) {
2422 uint nbits = t.hdr[sex].datlen;
2423 ubyte[1024U>>3U] wp_bits = 0;
2424 for (b = 0; b < nbits; b++) {
2425 if (t.getWriteProtected(t.hdr[sex].wp_start+b)) set_bit(wp_bits.ptr, b);
2427 fo.rawWriteExact(wp_bits[0..(nbits+7)>>3]);
2435 // //////////////////////////////////////////////////////////////////// //
2436 // `wdsnapbuf` and `wdsnapsize` should be set
2437 bool readDiskImage () nothrow @trusted {
2438 if (wdsnapsize < 16) return false; // alas
2439 // .scl?
2440 if ((cast(const(char)[])wdsnapbuf)[0..8] == "SINCLAIR") return readSCL();
2441 if ((cast(const(char)[])wdsnapbuf)[0..4] == "UDI!") return readUDI();
2442 if ((cast(const(char)[])wdsnapbuf)[0..3] == "FDI") return readFDI();
2443 if (wdsnapsize < 256*16) return false; // simple sanity check
2444 // assume .trd
2445 return readTRD();