Subversion Repositories pentevo

Rev

Blame | Last modification | View Log | Download | RSS feed | ?url?

  1. #include "std.h"
  2.  
  3. #include "emul.h"
  4. #include "vars.h"
  5. #include "wd93crc.h"
  6.  
  7. #include "util.h"
  8.  
  9. //static u8 old_status;
  10. //static __int64 last_t;
  11.  
  12. WD1793::WD1793()
  13. {
  14.     seldrive = &comp.fdd[0];
  15.     idx_cnt = 0;
  16.     idx_tmo = LLONG_MAX;
  17.     sign_status = 0;
  18.     tshift = 0;
  19.     EjectPending = false;
  20. }
  21.  
  22. void WD1793::process()
  23. {
  24.    time = comp.t_states + cpu.t;
  25.  
  26.    if(!(sign_status & SIG_HLD) && !(system & 0x20))
  27.    {
  28.        seldrive->motor = 0;
  29.    }
  30.  
  31.    if (seldrive->rawdata)
  32.        status &= ~WDS_NOTRDY;
  33.    else
  34.        status |= WDS_NOTRDY;
  35.  
  36.    if (!(cmd & 0x80) || (cmd & 0xF0) == 0xD0) // seek / step / restore / interrupt commands
  37.    {
  38.       status &= ~WDS_INDEX;
  39.  
  40.       if(state != S_IDLE)
  41.       {
  42.           status &= ~(WDS_TRK00 | WDS_INDEX);
  43.           if (!seldrive->track) status |= WDS_TRK00;
  44.       }
  45.  
  46.       // todo: test spinning
  47.       // ╠шэшьры№эр  °шЁшэр шэфхъёэюую шьяєы№ёр 10ьъё яЁш f=2╠├Ў, 20ьъё яЁш f=1╠├Ў
  48.       if (seldrive->rawdata && seldrive->motor && ((time + tshift) % (Z80FQ / FDD_RPS) < (Z80FQ * 4 / 1000)))
  49.       {
  50.           // index every turn, len=4ms (if disk present)
  51.          if(state == S_IDLE)
  52.          {
  53.              if(time < idx_tmo)
  54.                  status |= WDS_INDEX;
  55.          }
  56.          else
  57.          {
  58.              status |= WDS_INDEX;
  59.          }
  60.       }
  61.  
  62. /*
  63.       if(((old_status ^ status) & WDS_INDEX))
  64.       {
  65.           printf("%9lld: %8lld: ip %s, cmd=0x%02x, dat=0x%02x, trk=0x%02x, st=0x%02x\n",
  66.               (time + tshift), ((time + tshift) - last_t ),
  67.               (status & WDS_INDEX) ? "0->1" : "1->0", cmd, data, track, status);
  68.           last_t = time + tshift;
  69.       }
  70. */
  71.    }
  72. //   old_status = status;
  73.  
  74.    for (;;)
  75.    {
  76.       if(!seldrive->motor)
  77.       {
  78.           status |= WDS_NOTRDY;
  79.       }
  80.       else
  81.       {
  82.           status &= ~WDS_NOTRDY;
  83.       }
  84.  
  85.       switch (state)
  86.       {
  87.  
  88.          // ----------------------------------------------------
  89.  
  90.          case S_IDLE:
  91.             status &= ~WDS_BUSY;
  92.             if(idx_cnt >= 15 || time > idx_tmo)
  93.             {
  94.                 idx_cnt = 15;
  95.                 status &= WDS_NOTRDY;
  96.                 status |= WDS_NOTRDY;
  97.                 seldrive->motor = 0;
  98.                 sign_status &= ~SIG_HLD;
  99.             }
  100.             rqs = INTRQ;
  101.             return;
  102.  
  103.          case S_WAIT:
  104.             if (time < next)
  105.                 return;
  106.             state = state2;
  107.             break;
  108.  
  109.          // ----------------------------------------------------
  110.          // ╧юфрэр ъюьрэфр Єшяр 2 шыш 3 (read/write)/(read am/read trk/write trk)
  111.          case S_DELAY_BEFORE_CMD:
  112.             if (!conf.wd93_nodelay && (cmd & CMD_DELAY)) // ╧ЁютхЁър сшЄр E=1
  113.                 next += (Z80FQ*15/1000); // 15ms delay
  114.             state2 = S_WAIT_HLT_RW;
  115.             state = S_WAIT;
  116.             break;
  117.  
  118.          case S_WAIT_HLT_RW:
  119.             if(!(system & 0x08)) // HLT = 0 (схёъюэхўэюх юцшфрэшх HLT)
  120.             {
  121.                 return;
  122.             }
  123.  
  124.             state = S_CMD_RW;
  125.  
  126.          case S_CMD_RW:
  127.             if (((cmd & 0xE0) == 0xA0 || (cmd & 0xF0) == 0xF0) && conf.trdos_wp[drive])
  128.             {
  129.                status |= WDS_WRITEP;
  130.                state = S_IDLE;
  131.                break;
  132.             }
  133.  
  134.             if ((cmd & 0xC0) == 0x80 || (cmd & 0xF8) == 0xC0)
  135.             {
  136.                // read/write sectors or read am - find next AM
  137.                end_waiting_am = next + 5*Z80FQ/FDD_RPS; // max wait disk 5 turns
  138.                find_marker();
  139.                break;
  140.             }
  141.  
  142.             if ((cmd & 0xF8) == 0xF0) // write track
  143.             {
  144.                rqs = DRQ;
  145.                status |= WDS_DRQ;
  146.                next += 3*seldrive->t.ts_byte;
  147.                state2 = S_WRTRACK;
  148.                state = S_WAIT;
  149.                break;
  150.             }
  151.  
  152.             if ((cmd & 0xF8) == 0xE0) // read track
  153.             {
  154.                load();
  155.                rwptr = 0;
  156.                rwlen = seldrive->t.trklen;
  157.                state2 = S_READ;
  158.                getindex();
  159.                break;
  160.             }
  161.  
  162.             // else unknown command
  163.             state = S_IDLE;
  164.             break;
  165.  
  166.         case S_FOUND_NEXT_ID:
  167.             if (!seldrive->rawdata)
  168.             { // no disk - wait again
  169.                end_waiting_am = next + 5*Z80FQ/FDD_RPS;
  170.         nextmk:
  171.                find_marker();
  172.                break;
  173.             }
  174.             if (next >= end_waiting_am)
  175.             {
  176.             nf:
  177.                 status |= WDS_NOTFOUND;
  178.                 state = S_IDLE;
  179.                 break;
  180.             }
  181.             if (foundid == -1U)
  182.                 goto nf;
  183.  
  184.             status &= ~WDS_CRCERR;
  185.             load();
  186.  
  187.             if (!(cmd & 0x80)) // verify after seek
  188.             {
  189.                if (seldrive->t.hdr[foundid].c != track)
  190.                    goto nextmk;
  191.                if (!seldrive->t.hdr[foundid].c1)
  192.                {
  193.                    status |= WDS_CRCERR;
  194.                    goto nextmk;
  195.                }
  196.                state = S_IDLE; break;
  197.             }
  198.  
  199.             if ((cmd & 0xF0) == 0xC0) // read AM
  200.             {
  201.                rwptr = unsigned(seldrive->t.hdr[foundid].id - seldrive->t.trkd);
  202.                rwlen = 6;
  203.  
  204.          read_first_byte:
  205.                u8 data_mask = 0;
  206.                if(seldrive->t.trkwp) // яЁютхЁ хь ърЁЄє ёсющэ√ї срщЄют
  207.                {
  208.                    if(seldrive->t.test_wp(rwptr))
  209.                    {
  210.                        data_mask = 0xFF;
  211.                        seldrive->t.hdr[foundid].c1 = 0; // bad address crc
  212.                    }
  213.                }
  214.                data = seldrive->t.trkd[rwptr++];
  215.                data ^= data_mask;
  216.                rwlen--;
  217.                rqs = DRQ; status |= WDS_DRQ;
  218.                next += seldrive->t.ts_byte;
  219.                state = S_WAIT;
  220.                state2 = S_READ;
  221.                break;
  222.             }
  223.  
  224.             // else R/W sector(s)
  225.             if (seldrive->t.hdr[foundid].c != track || seldrive->t.hdr[foundid].n != sector)
  226.                 goto nextmk;
  227.             if ((cmd & CMD_SIDE_CMP_FLAG) && (((cmd >> CMD_SIDE_SHIFT) ^ seldrive->t.hdr[foundid].s) & 1))
  228.                 goto nextmk;
  229.             if (!seldrive->t.hdr[foundid].c1)
  230.             {
  231.                 status |= WDS_CRCERR;
  232.                 goto nextmk;
  233.             }
  234.  
  235.             if (cmd & 0x20) // write sector(s)
  236.             {
  237.                rqs = DRQ; status |= WDS_DRQ;
  238.                next += seldrive->t.ts_byte*9;
  239.                state = S_WAIT; state2 = S_WRSEC;
  240.                break;
  241.             }
  242.  
  243.             // read sector(s)
  244.             if (!seldrive->t.hdr[foundid].data)
  245.                 goto nextmk; // ╤хъЄюЁ схч чюэ√ фрээ√ї
  246.             if(!conf.wd93_nodelay)
  247.                 next += seldrive->t.ts_byte*(seldrive->t.hdr[foundid].data - seldrive->t.hdr[foundid].id); // ╟рфхЁцър эр яЁюяєёъ чруюыютър ёхъЄюЁр ш яЁюсхыр ьхцфє чруюыютъюь ш чюэющ фрээ√ї
  248.             state = S_WAIT;
  249.             state2 = S_RDSEC;
  250.             break;
  251.  
  252.          case S_RDSEC:
  253.             if (seldrive->t.hdr[foundid].data[-1] == 0xF8)
  254.                 status |= WDS_RECORDT;
  255.             else
  256.                 status &= ~WDS_RECORDT;
  257.             rwptr = unsigned(seldrive->t.hdr[foundid].data - seldrive->t.trkd); // ╤ьх∙хэшх чюэ√ фрээ√ї ёхъЄюЁр (т срщЄрї) юЄэюёшЄхы№эю эрўрыр ЄЁхър
  258.             rwlen = 128U << (seldrive->t.hdr[foundid].l & 3); // [vv]
  259.             goto read_first_byte;
  260.  
  261.          case S_READ:
  262.             if (notready())
  263.                 break;
  264.             load();
  265.  
  266.             if(!seldrive->t.trkd)
  267.             {
  268.                 status |= WDS_NOTFOUND;
  269.                 state = S_IDLE;
  270.                 break;
  271.             }
  272.  
  273.             if (rwlen)
  274.             {
  275.                trdos_load = ROMLED_TIME;
  276.                if (rqs & DRQ)
  277.                    status |= WDS_LOST;
  278.  
  279.                u8 data_mask = 0;
  280.                if(seldrive->t.trkwp) // яЁютхЁ хь ърЁЄє ёсющэ√ї срщЄют
  281.                {
  282.                    if(seldrive->t.test_wp(rwptr))
  283.                    {
  284.                        data_mask = 0xFF;
  285.                        if ((cmd & 0xE0) == 0x80) // read sector
  286.                        {
  287.                            seldrive->t.hdr[foundid].c2 = 0; // bad data crc
  288.                        }
  289.                        if ((cmd & 0xF0) == 0xC0) // read address
  290.                        {
  291.                            seldrive->t.hdr[foundid].c1 = 0; // bad address crc
  292.                        }
  293.                    }
  294.                }
  295.                data = seldrive->t.trkd[rwptr++];
  296.                data ^= data_mask;
  297.                rwlen--;
  298.                rqs = DRQ;
  299.                status |= WDS_DRQ;
  300.  
  301.                if (!conf.wd93_nodelay)
  302.                    next += seldrive->t.ts_byte;
  303.                else
  304.                    next = time + 1;
  305.                state = S_WAIT;
  306.                state2 = S_READ;
  307.             }
  308.             else
  309.             {
  310.                if ((cmd & 0xE0) == 0x80) // read sector
  311.                {
  312.                   if (!seldrive->t.hdr[foundid].c2)
  313.                       status |= WDS_CRCERR;
  314.                   if (cmd & CMD_MULTIPLE)
  315.                   {
  316.                       sector++;
  317.                       state = S_CMD_RW;
  318.                       break;
  319.                   }
  320.                }
  321.  
  322.                // FIXME: ┬Ёхьхээ√щ їръ фы  zx-format 3
  323.                if (((cmd & 0xF8) == 0xE0) && (seldrive->t.trklen < MAX_TRACK_LEN)) // read track
  324.                {
  325.                    status |= WDS_LOST;
  326.                }
  327.  
  328.                if ((cmd & 0xF0) == 0xC0) // read address
  329.                {
  330.                   if (!seldrive->t.hdr[foundid].c1)
  331.                       status |= WDS_CRCERR;
  332.                }
  333.                state = S_IDLE;
  334.             }
  335.             break;
  336.  
  337.  
  338.          case S_WRSEC:
  339.             load();
  340.             if (rqs & DRQ)
  341.             {
  342.                 status |= WDS_LOST;
  343.                 state = S_IDLE;
  344.                 break;
  345.             }
  346.             seldrive->optype |= 1;
  347.             rwptr = unsigned(seldrive->t.hdr[foundid].id + 6 + 11 + 11 - seldrive->t.trkd);
  348.             for (rwlen = 0; rwlen < 12; rwlen++)
  349.                 seldrive->t.write(rwptr++, 0, 0);
  350.             for (rwlen = 0; rwlen < 3; rwlen++)
  351.                 seldrive->t.write(rwptr++, 0xA1, 1);
  352.             seldrive->t.write(rwptr++, (cmd & CMD_WRITE_DEL)? 0xF8 : 0xFB, 0);
  353.             rwlen = 128U << (seldrive->t.hdr[foundid].l & 3); // [vv]
  354.             state = S_WRITE;
  355.             break;
  356.  
  357.          case S_WRITE:
  358.             if (notready())
  359.                 break;
  360.             if(rqs & DRQ)
  361.             {
  362.                 status |= WDS_LOST;
  363.                 data = 0;
  364.             }
  365.             trdos_save = ROMLED_TIME;
  366.             seldrive->t.write(rwptr++, data, 0); rwlen--;
  367.             if (rwptr == seldrive->t.trklen) rwptr = 0;
  368.             seldrive->t.sf = JUST_SEEK; // invalidate sectors
  369.             if (rwlen)
  370.             {
  371.                if (!conf.wd93_nodelay) next += seldrive->t.ts_byte;
  372.                state = S_WAIT; state2 = S_WRITE;
  373.                rqs = DRQ; status |= WDS_DRQ;
  374.             }
  375.             else
  376.             {
  377.                unsigned len = (128U << (seldrive->t.hdr[foundid].l & 3)) + 1; //[vv]
  378.                unsigned char sc[2056];
  379.                if (rwptr < len)
  380.                {
  381.                    memcpy(sc, seldrive->t.trkd + seldrive->t.trklen - rwptr, rwptr);
  382.                    memcpy(sc + rwptr, seldrive->t.trkd, len - rwptr);
  383.                }
  384.                else
  385.                    memcpy(sc, seldrive->t.trkd + rwptr - len, len);
  386.                unsigned crc = wd93_crc(sc, len);
  387.                seldrive->t.write(rwptr++, (BYTE)crc, 0);
  388.                seldrive->t.write(rwptr++, (BYTE)(crc >> 8), 0);
  389.                seldrive->t.write(rwptr, 0xFF, 0);
  390.                if (cmd & CMD_MULTIPLE)
  391.                {
  392.                    sector++;
  393.                    state = S_CMD_RW;
  394.                    break;
  395.                }
  396.                state = S_IDLE;
  397.             }
  398.             break;
  399.  
  400.          case S_WRTRACK:
  401.             if (rqs & DRQ)
  402.             {
  403.                 status |= WDS_LOST;
  404.                 state = S_IDLE;
  405.                 break;
  406.             }
  407.             seldrive->optype |= 2;
  408.             state2 = S_WR_TRACK_DATA;
  409.             start_crc = 0;
  410.             getindex();
  411.             end_waiting_am = next + 5 * Z80FQ /FDD_RPS;
  412.          break;
  413.  
  414.          case S_WR_TRACK_DATA:
  415.          {
  416.             if (notready())
  417.                 break;
  418.             trdos_format = ROMLED_TIME;
  419.             if (rqs & DRQ)
  420.             {
  421.                 status |= WDS_LOST;
  422.                 data = 0;
  423.             }
  424.             seldrive->t.seek(seldrive, seldrive->track, side, JUST_SEEK);
  425.             seldrive->t.sf = JUST_SEEK; // invalidate sectors
  426.  
  427.             if(!seldrive->t.trkd)
  428.             {
  429.                 state = S_IDLE;
  430.                 break;
  431.             }
  432.  
  433.             unsigned char marker = 0, byte = data;
  434.             unsigned crc;
  435.             switch(data)
  436.             {
  437.             case 0xF5:
  438.                 byte = 0xA1;
  439.                 marker = 1;
  440.                 start_crc = rwptr + 1;
  441.             break;
  442.  
  443.             case 0xF6:
  444.                 byte = 0xC2;
  445.                 marker = 1;
  446.             break;
  447.  
  448.             case 0xF7:
  449.                 crc = wd93_crc(seldrive->t.trkd + start_crc, rwptr - start_crc);
  450.                 byte = crc & 0xFF;
  451.             break;
  452.             }
  453.  
  454.             seldrive->t.write(rwptr++, byte, marker);
  455.             rwlen--;
  456.             if (data == 0xF7)
  457.             {
  458.                 seldrive->t.write(rwptr++, (crc >> 8) & 0xFF, 0);
  459.                 rwlen--; // second byte of CRC16
  460.             }
  461.  
  462.             if ((int)rwlen > 0)
  463.             {
  464.                if (!conf.wd93_nodelay)
  465.                    next += seldrive->t.ts_byte;
  466.                state2 = S_WR_TRACK_DATA;
  467.                state = S_WAIT;
  468.                rqs = DRQ;
  469.                status |= WDS_DRQ;
  470.                break;
  471.             }
  472.             state = S_IDLE;
  473.             break;
  474.          }
  475.  
  476.          // ----------------------------------------------------
  477.  
  478.          case S_TYPE1_CMD: // ╧юфрэр ъюьрэфр Єшяр 1 (restore/seek/step)
  479.             status &= ~(WDS_CRCERR | WDS_SEEKERR | WDS_WRITEP);
  480.             rqs = 0;
  481.  
  482.             if (conf.trdos_wp[drive])
  483.                 status |= WDS_WRITEP;
  484.  
  485.             seldrive->motor = (cmd & CMD_SEEK_HEADLOAD) || (system & 0x20) ? next + 2*Z80FQ : 0;
  486.  
  487.             state2 = S_SEEKSTART; // default is seek/restore
  488.  
  489.             if (cmd & 0xE0)  // single step
  490.             {
  491.                if (cmd & 0x40) // step in / step out
  492.                    stepdirection = (cmd & CMD_SEEK_DIR) ? -1 : 1;
  493.                state2 = S_STEP;
  494.             }
  495.  
  496.             next += (Z80FQ*14)/1000000;
  497.             state = S_WAIT; // ╟рфхЁцър 14ьъё яхЁхф яю тыхэхшь ёЄрЄєёр BUSY
  498.          break;
  499.  
  500.  
  501.          case S_STEP:
  502.          {
  503.             status |= WDS_BUSY;
  504.             trdos_seek = ROMLED_TIME;
  505.  
  506.             if (seldrive->track == 0 && stepdirection < 0) // яЁютхЁър TRK00
  507.             {
  508.                 track = 0;
  509.                 state = S_VERIFY; // ╬сЁрсюЄър сшЄр ъюьрэф√ V
  510.                 break;
  511.             }
  512.  
  513.             // ╬сЁрсюЄърсшЄр T=1 (фы  seek тёхуфр 1, фы  restore тёхуфр 0, эю юсэютыхэшх ЁхушёЄЁр ЄЁ¤ър фхырхЄё )
  514.             if (!(cmd & 0xF0) || (cmd & CMD_SEEK_TRKUPD))
  515.                 track += stepdirection;
  516.  
  517.             if(seldrive->motor)
  518.             {
  519.                 seldrive->track += stepdirection;
  520.                 if (seldrive->track == (unsigned char)-1)
  521.                     seldrive->track = 0;
  522.                 if (seldrive->track >= MAX_PHYS_CYL)
  523.                     seldrive->track = MAX_PHYS_CYL;
  524.                 seldrive->t.clear();
  525.             }
  526.  
  527.             static const unsigned steps[] = { 6,12,20,30 };
  528.             if (!conf.wd93_nodelay)
  529.                 next += steps[cmd & CMD_SEEK_RATE]*Z80FQ/1000;
  530.  
  531.             #ifndef MOD_9X
  532.             if (!conf.wd93_nodelay && conf.fdd_noise)
  533.                 Beep((stepdirection > 0)? 600 : 800, 2);
  534.             #endif
  535.  
  536.             state2 = (cmd & 0xE0) ? S_VERIFY /*╩юьрэфр step*/: S_SEEK /*╩юьрэфр seek/restore*/;
  537.             state = S_WAIT;
  538.             break;
  539.          }
  540.  
  541.          case S_SEEKSTART:
  542.             status |= WDS_BUSY;
  543.  
  544.             if (!(cmd & 0x10)) // ╩юьрэфр restore
  545.             {
  546.                 if (!conf.wd93_nodelay)
  547.                 {
  548.                     state2 = S_RESTORE;
  549.                     next += (Z80FQ*21)/1000000; // чрфхЁцър ~21ьъё яхЁхф чруЁєчъющ ЁхушёЄЁр ЄЁ¤ър
  550.                     state = S_WAIT;
  551.                     break;
  552.                 }
  553.  
  554.                 state = S_RESTORE;
  555.                 break;
  556.             }
  557.             state = S_SEEK;
  558.             break;
  559.  
  560.          case S_RESTORE:
  561.             track = 0xFF;
  562.             data = 0;
  563.             state = S_SEEK;
  564.             break;
  565.  
  566.          case S_SEEK:
  567.             if (data == track)
  568.             {
  569.                 state = S_VERIFY;
  570.                 break;
  571.             }
  572.             stepdirection = (data < track) ? -1 : 1;
  573.             state = S_STEP;
  574.             break;
  575.  
  576.          case S_VERIFY:
  577.             if (!(cmd & CMD_SEEK_VERIFY)) // ╧ЁютхЁър эюьхЁр ЄЁ¤ър эх эєцэр V=0
  578.             {
  579.                 status |= WDS_BUSY;
  580.                 state2 = S_IDLE;
  581.                 state = S_WAIT;
  582.                 idx_tmo = next + 15 * Z80FQ/FDD_RPS; // 15 disk turns
  583.                 next += (105*Z80FQ)/1000000; // ╟рфхЁцър 105ьъё ёю ёЄрЄєёюь BUSY
  584.                 break;
  585.             }
  586.  
  587.             // ╧ЁютхЁър эюьхЁр ЄЁ¤ър эєцэр V=1
  588.             sign_status |= SIG_HLD;
  589.  
  590.             // ╬цшфрэшх 15ьё
  591.             //       |
  592.             //       v
  593.             // ╬цшфрэшх HLT == 1
  594.             //       |
  595.             //       v
  596.             // S_VERIFY2
  597.             if (!conf.wd93_nodelay)
  598.             {
  599.                 next += (15*Z80FQ)/1000; // ╟рфхЁцър 15ьё
  600.                 state2 = S_WAIT_HLT;
  601.                 state = S_WAIT;
  602.                 break;
  603.             }
  604.             state = S_WAIT_HLT;
  605.  
  606.          case S_WAIT_HLT:
  607.             if(!(system & 0x08)) // HLT = 0 (схёъюэхўэюх юцшфрэшх HLT)
  608.             {
  609.                 return;
  610.             }
  611.             state = S_VERIFY2;
  612.  
  613.          case S_VERIFY2:
  614.             end_waiting_am = next + 6*Z80FQ/FDD_RPS; // max wait disk 6 turns
  615.             load();
  616.             find_marker();
  617.             break;
  618.  
  619.          // ----------------------------------------------------
  620.  
  621.          case S_EJECT1:
  622.              next = time + 10 * (Z80FQ / 1000); // ╟рфхЁцър 10ьё
  623.  
  624.              state2 = S_EJECT2;
  625.              state = S_WAIT;
  626.              break;
  627.  
  628.          case S_EJECT2:
  629.              status &= ~WDS_WRITEP;
  630.  
  631.              EjectPending = false;
  632.              state = S_IDLE;
  633.              break;
  634.  
  635.          // ----------------------------------------------------
  636.  
  637.          default:
  638.             errexit("WD1793 in wrong state");
  639.       }
  640.    }
  641. }
  642.  
  643. void WD1793::find_marker()
  644. {
  645.    if (conf.wd93_nodelay && seldrive->track != track)
  646.        seldrive->track = track;
  647.    load();
  648.  
  649.    foundid = -1U;
  650.    if (seldrive->motor && seldrive->rawdata)
  651.    {
  652.       unsigned div = seldrive->t.trklen*seldrive->t.ts_byte; // ─ышэр фюЁюцъш т ЄръЄрї cpu
  653.       unsigned i = (unsigned)((next+tshift) % div) / seldrive->t.ts_byte; // ╧ючшЎш  срщЄр ёююЄтхЄёЄтє■∙хую Єхъє∙хьє ЄръЄє эр фюЁюцъх
  654.       unsigned wait = -1U;
  655.  
  656.       // ╧юшёъ чруюыютър ьшэшьры№эю юЄёЄю ∙хую юЄ Єхъє∙хую срщЄр
  657.       for (unsigned is = 0; is < seldrive->t.s; is++)
  658.       {
  659.          unsigned pos = unsigned(seldrive->t.hdr[is].id - seldrive->t.trkd); // ╤ьх∙хэшх (т срщЄрї) чруюыютър юЄэюёшЄхы№эю эрўрыр фюЁюцъш
  660.          unsigned dist = (pos > i)? pos-i : seldrive->t.trklen+pos-i; // ╨рёёЄю эшх (т срщЄрї) юЄ чруюыютър фю Єхъє∙хую срщЄр
  661.          if (dist < wait)
  662.          {
  663.              wait = dist;
  664.              foundid = is;
  665.          }
  666.       }
  667.  
  668.       if (foundid != -1U)
  669.           wait *= seldrive->t.ts_byte; // ╟рфхЁцър т ЄръЄрї юЄ Єхъє∙хую ЄръЄр фю ЄръЄр ўЄхэш  яхЁтюую срщЄр чруюыютър
  670.       else
  671.           wait = 10*Z80FQ/FDD_RPS;
  672.  
  673.       if (conf.wd93_nodelay && foundid != -1U)
  674.       {
  675.          // adjust tshift, that id appares right under head
  676.          unsigned pos = unsigned(seldrive->t.hdr[foundid].id - seldrive->t.trkd + 2);
  677.          tshift = (unsigned)(((pos * seldrive->t.ts_byte) - (next % div) + div) % div);
  678.          wait = 100; // delay=0 causes fdc to search infinitely, when no matched id on track
  679.       }
  680.  
  681.       next += wait;
  682.    } // else no index pulses - infinite wait
  683.    else
  684.    {
  685.        next = comp.t_states + cpu.t + 1;
  686.    }
  687.  
  688.    if (seldrive->rawdata && next > end_waiting_am)
  689.    {
  690.        next = end_waiting_am;
  691.        foundid = -1U;
  692.    }
  693.    state = S_WAIT;
  694.    state2 = S_FOUND_NEXT_ID;
  695. }
  696.  
  697. char WD1793::notready()
  698. {
  699.    // fdc is too fast in no-delay mode, wait until cpu handles DRQ, but not more 'end_waiting_am'
  700.    if (conf.wd93_nodelay && (rqs & DRQ) && (next < (end_waiting_am - 5*Z80FQ/FDD_RPS) + (600*Z80FQ)/1000))
  701.    {
  702.        state2 = state;
  703.        state = S_WAIT;
  704.        next += seldrive->t.ts_byte;
  705.        return 1;
  706.    }
  707.  
  708.    return 0;
  709. }
  710.  
  711. void WD1793::getindex()
  712. {
  713.    unsigned trlen = seldrive->t.trklen*seldrive->t.ts_byte;
  714.    unsigned ticks = (unsigned)((next+tshift) % trlen);
  715.    if (!conf.wd93_nodelay)
  716.        next += (trlen - ticks);
  717.    rwptr = 0;
  718.    rwlen = seldrive->t.trklen;
  719.    state = S_WAIT;
  720. }
  721.  
  722. void WD1793::load()
  723. {
  724.    seldrive->t.seek(seldrive, seldrive->track, side, LOAD_SECTORS);
  725. }
  726.  
  727. unsigned char WD1793::in(unsigned char port)
  728. {
  729.    process();
  730.    if (port & 0x80)
  731.        return rqs | (system & 0x3F);
  732.    if (port == 0x1F)
  733.    {
  734.        rqs &= ~INTRQ;
  735.        return RdStatus();
  736.    }
  737.    if (port == 0x3F)
  738.        return track;
  739.    if (port == 0x5F)
  740.        return sector;
  741.    if (port == 0x7F)
  742.    {
  743.        status &= ~WDS_DRQ;
  744.        rqs &= ~DRQ;
  745.        return data;
  746.    }
  747.    return 0xFF;
  748. }
  749.  
  750. u8 WD1793::RdStatus()
  751. {
  752.     if(!(cmd & 0x80))
  753.     {
  754.         // hld & hlt
  755.         return status | (((sign_status & SIG_HLD) && (system & 8)) ? WDS_HEADL : 0);
  756.     }
  757.     return status;
  758. }
  759.  
  760.  
  761. void WD1793::out(unsigned char port, unsigned char val)
  762. {
  763.    process();
  764.  
  765.    if(EjectPending)
  766.    {
  767.        return;
  768.    }
  769.  
  770.    if (port == 0x1F)
  771.    { // cmd
  772. //       printf("%9lld: cmd=0x%02x, dat=0x%02x, trk=0x%02x, st=0x%02x\n", time + tshift, val, data, track, status);
  773.  
  774.       // force interrupt (type 4)
  775.       if ((val & 0xF0) == 0xD0)
  776.       {
  777.          u8 Cond = (val & 0xF);
  778.          next = comp.t_states + cpu.t;
  779.          idx_cnt = 0;
  780.          idx_tmo = next + 15 * Z80FQ/FDD_RPS; // 15 disk turns
  781.          cmd = val;
  782.  
  783.          if(Cond == 0)
  784.          {
  785.              state = S_IDLE; rqs = 0;
  786.              status &= ~WDS_BUSY;
  787.              return;
  788.          }
  789.  
  790.          if(Cond & 8) // unconditional int
  791.          {
  792.              state = S_IDLE; rqs = INTRQ;
  793.              status &= ~WDS_BUSY;
  794.              return;
  795.          }
  796.  
  797.          if(Cond & 4) // int by idam (unimplemented yet)
  798.          {
  799.              state = S_IDLE; rqs = INTRQ;
  800.              status &= ~WDS_BUSY;
  801.              return;
  802.          }
  803.  
  804.          if(Cond & 2) // int 1->0 rdy (unimplemented yet)
  805.          {
  806.              state = S_IDLE; rqs = INTRQ;
  807.              status &= ~WDS_BUSY;
  808.              return;
  809.          }
  810.  
  811.          if(Cond & 1) // int 0->1 rdy (unimplemented yet)
  812.          {
  813.              state = S_IDLE; rqs = INTRQ;
  814.              status &= ~WDS_BUSY;
  815.              return;
  816.          }
  817.  
  818.          return;
  819.       }
  820.  
  821.       if (status & WDS_BUSY)
  822.           return;
  823.       cmd = val;
  824.       next = comp.t_states + cpu.t;
  825.       status |= WDS_BUSY;
  826.       rqs = 0;
  827.       idx_cnt = 0;
  828.       idx_tmo = LLONG_MAX;
  829.  
  830.       //-----------------------------------------------------------------------
  831.  
  832.       if (cmd & 0x80) // read/write command (type 2, 3)
  833.       {
  834.          status = (status | WDS_BUSY) & ~(WDS_DRQ | WDS_LOST | WDS_NOTFOUND | WDS_RECORDT | WDS_WRITEP);
  835.  
  836.          // continue disk spinning
  837.          seldrive->motor = next + 2*Z80FQ;
  838.  
  839.          // abort if no disk
  840.          if (status & WDS_NOTRDY)
  841.          {
  842.              state2 = S_IDLE;
  843.              state = S_WAIT;
  844.              next = comp.t_states + cpu.t + Z80FQ/FDD_RPS;
  845.              rqs = INTRQ;
  846.              return;
  847.          }
  848.  
  849.          sign_status |= SIG_HLD;
  850.  
  851.          state = S_DELAY_BEFORE_CMD;
  852.          return;
  853.       }
  854.  
  855.       // (type 1)
  856.       if(cmd & CMD_SEEK_HEADLOAD) // h = 1
  857.           sign_status |= SIG_HLD;
  858.       else
  859.           sign_status &= ~SIG_HLD;
  860.  
  861.       // else seek/step command
  862.       status &= ~WDS_BUSY;
  863.       state = S_TYPE1_CMD;
  864.       return;
  865.    }
  866.  
  867.    //=======================================================================
  868.  
  869.    if (port == 0x3F)
  870.    {
  871.        track = val;
  872.        return;
  873.    }
  874.  
  875.    if (port == 0x5F)
  876.    {
  877.        sector = val;
  878.        return;
  879.    }
  880.  
  881.    if (port == 0x7F)
  882.    {
  883.        data = val;
  884.        rqs &= ~DRQ;
  885.        status &= ~WDS_DRQ;
  886.        return;
  887.    }
  888.  
  889.    if (port & 0x80) // FF
  890.    { // system
  891.       drive = val & 3;
  892.       side = ~(val >> 4) & 1;
  893.       seldrive = &comp.fdd[drive];
  894.       seldrive->t.clear();
  895.  
  896.       if (!(val & 0x04))
  897.       { // reset
  898.          status &= ~WDS_NOTRDY;
  899.          rqs = INTRQ;
  900.          seldrive->motor = 0;
  901.          idx_cnt = 0;
  902.  
  903.          // ╚ч юяшёрэш  ёшуэрыр /MR FD 179X-02 floppy disk formatter/controller (WD may 80)
  904.          state = S_TYPE1_CMD;
  905.          cmd = 3; // restore
  906.          sector = 1;
  907.       }
  908.       else
  909.       {
  910.           if((system ^ val) & SYS_HLT) // hlt 0->1
  911.           {
  912.               if(!(status & WDS_BUSY))
  913.               {
  914.                   idx_cnt++; // ├хэхЁрЎш  шэфхъёэюую шьяєы№ёр ўхЁхч фшюф т ъюэЄЁюыыхЁх шфє∙шщ ё сшЄр HLT яюЁЄр FF эр /index
  915.               }
  916.           }
  917.       }
  918.  
  919.       if(val & 0x20) // ┬ quorum сшЄ D5 єяЁрты хЄ ьюЄюЁюь фшёъютюфр
  920.       {
  921.           sign_status |= SIG_HLD;
  922.           seldrive->motor = next + 2*Z80FQ;
  923.       }
  924.       system = val;
  925.    }
  926. }
  927.  
  928. void WD1793::Eject(unsigned Drive)
  929. {
  930.     if(seldrive == &comp.fdd[Drive]) //├хэхЁрЎш  яюёыхфютрЄхы№эюёЄш т√эшьрэш  фшёъхЄ√ (/WPRT->1->0)
  931.     {
  932.         status |= WDS_WRITEP;
  933.         state = S_EJECT1;
  934.         EjectPending = true;
  935.     }
  936.     comp.fdd[Drive].free();
  937. }
  938.  
  939. void WD1793::trdos_traps()
  940. {
  941.    unsigned pc = (cpu.pc & 0xFFFF);
  942.    if (pc < 0x3DFD)
  943.        return;
  944.  
  945.    // ╧ючшЎшюэшЁютрэшх эр ёюёхфэ■■ фюЁюцъє (ярєчр)
  946.    if (pc == 0x3DFD && bankr[0][0x3DFD] == 0x3E && bankr[0][0x3DFF] == 0x0E)
  947.    {
  948.        cpu.pc = cpu.DbgMemIf->rm(cpu.sp++);
  949.        cpu.pc |= unsigned(cpu.DbgMemIf->rm(cpu.sp++) << 8U);
  950.        cpu.a = 0;
  951.        cpu.c = 0;
  952.    }
  953.  
  954.    // ╧ючшЎшюэшЁютрэшх эр яЁюшчтюы№эє■ фюЁюцъє (ярєчр)
  955.    if (pc == 0x3EA0 && bankr[0][0x3EA0] == 0x06 && bankr[0][0x3EA2] == 0x3E)
  956.    {
  957.        cpu.pc = cpu.DbgMemIf->rm(cpu.sp++);
  958.        cpu.pc |= unsigned(cpu.DbgMemIf->rm(cpu.sp++) << 8U);
  959.        cpu.a = 0;
  960.        cpu.b = 0;
  961.    }
  962.  
  963.    if (pc == 0x3E01 && bankr[0][0x3E01] == 0x0D)
  964.    {
  965.        cpu.a = cpu.c = 1;
  966.        return;
  967.    } // no delays
  968.  
  969.    if (pc == 0x3FEC && bankr[0][0x3FED] == 0xA2 &&
  970.               (state == S_READ || (state2 == S_READ && state == S_WAIT)))
  971.    {
  972.       trdos_load = ROMLED_TIME;
  973.       if (rqs & DRQ)
  974.       {
  975.          cpu.DbgMemIf->wm(cpu.hl, data); // move byte from controller
  976.          cpu.hl++;
  977.          cpu.b--;
  978.          rqs &= ~DRQ;
  979.          status &= ~WDS_DRQ;
  980.       }
  981.  
  982.       if(seldrive->t.trkd)
  983.       {
  984.           while (rwlen)
  985.           { // move others
  986.              cpu.DbgMemIf->wm(cpu.hl, seldrive->t.trkd[rwptr++]);
  987.              rwlen--;
  988.              cpu.hl++;
  989.              cpu.b--;
  990.           }
  991.       }
  992.       cpu.pc += 2; // skip INI
  993.       return;
  994.    }
  995.    if (pc == 0x3FD1 && bankr[0][0x3FD2] == 0xA3 &&
  996.               (rqs & DRQ) && (rwlen>1) && (state == S_WRITE || (state2 == S_WRITE && state == S_WAIT)))
  997.    {
  998.       trdos_save = ROMLED_TIME;
  999.       while (rwlen > 1)
  1000.       {
  1001.          seldrive->t.write(rwptr++, cpu.DbgMemIf->rm(cpu.hl), 0);
  1002.          rwlen--;
  1003.          cpu.hl++;
  1004.          cpu.b--;
  1005.       }
  1006.       cpu.pc += 2; // skip OUTI
  1007.       return;
  1008.    }
  1009. }
  1010.