#include "std.h"
#include "emul.h"
#include "vars.h"
#include "util.h"
#include "upd765.h"
#if 0
#define dprintf printf
#else
#define dprintf(...)
#endif
static unsigned CalcCmdLen(u8 Cmd)
{
Cmd &= 0x1F;
unsigned CmdLen;
switch(Cmd)
{
case 0b00010: CmdLen = 8; break; // read track
case 0b00011: CmdLen = 2; break; // specify
case 0b00100: CmdLen = 1; break; // sense drive status
case 0b00101: CmdLen = 8; break; // write data
case 0b00110: CmdLen = 8; break; // read data
case 0b00111: CmdLen = 1; break; // recalibrate
case 0b01000: CmdLen = 0; break; // sense interrupt status
case 0b01001: CmdLen = 8; break; // write deleted data
case 0b01010: CmdLen = 1; break; // read id
case 0b01100: CmdLen = 8; break; // read deleted data
case 0b01101: CmdLen = 5; break; // format track
case 0b01111: CmdLen = 2; break; // seek
case 0b10001: CmdLen = 8; break; // scan equal
case 0b11001: CmdLen = 8; break; // scan low or equal
case 0b11101: CmdLen = 8; break; // scan high or equal
default: CmdLen = 0;
}
return CmdLen;
}
TUpd765::TUpd765()
{
SelDrive = &comp.fdd[0];
}
void TUpd765::out(u8 data)
{
// 3FFD (ЁхушёЄЁ ъюьрэф/фрээ√ї)
if(Msr & (MSR_CB | MSR_EXM))
{
dprintf("upd765: error fdc is busy, d=0x%02X, msr=0x%02X\n", data, Msr);
return;
}
if(DataPtr < _countof(Data))
{
if(DataPtr == 0) // ╧хЁхфрхЄё ъюф ъюьрэф√
{
CmdLen = CalcCmdLen(data);
}
Data[DataPtr++] = data;
}
if(DataPtr == CmdLen + 1) // execute command
{
Msr |= MSR_CB;
ResultPtr = 0;
ReadData = false;
// ╟р∙шЄ√ шёяюы№чє■Є ЇръЄ, ўЄю т +3 шёяюы№чєхЄё Єюы№ъю ьырф°шщ сшЄ шч us
// ╧ю¤Єюьє яЁш us=2 т√сшЁрхЄё ЄюЄ цх фшёъютюф ўЄю ш яЁш us=0
// untouchables, batman the movie, chase h.q.
auto Cmd = Data[0] & 0x1F;
switch(Cmd)
{
case 0b00011: // specify
dprintf("upd765: specify: srt=%u, hut=%u, hlt=%u, nd=%u\n", Data[1] >> 4U, Data[1] & 0xFU, Data[2] >> 1U, Data[2] & 1U);
Msr &= ~(MSR_DIO | MSR_CB); // CPU->FDC
break;
case 0b00100: // sense drv st
{
auto us{ (Data[1] & 3U) };
auto hd{ (Data[1] >> 2U) & 1U };
SelDrive = &comp.fdd[us];
St3 &= ~3;
St3 |= ST3_RY | us;
if(pc[us] == 0)
{
St3 |= ST3_T0;
}
else
{
St3 &= ~ST3_T0;
}
Result[0] = &St3;
ResultLen = 1;
Msr |= MSR_RQM | MSR_DIO; // CPU<-FDC
dprintf("upd765: sense drv st: hd=%u, us=%u, st3=0x%02X\n", hd, us, St3);
break;
}
case 0b00110: // read data
case 0b01100: // read deleted data
ReadData = true;
case 0b00010: // read track
{
c = Data[2];
h = Data[3];
r = Data[4];
n = Data[5];
auto mt = (Data[0] >> 7U) & 1U;
auto sk = (Data[0] >> 5U) & 1U;
auto us = Data[1] & 1U;
SelDrive = &comp.fdd[us];
ph[us] = (Data[1] >> 2U) & 1U;
eot = Data[6];
const char *CmdStr{ };
switch(Cmd)
{
case 0b00010: CmdStr = "track"; break; // read track
case 0b00110: CmdStr = "data"; break; // read data
case 0b01100: CmdStr = "deleted data"; break; // read deleted data
}
load();
auto SecHdr = SelDrive->t.get_sector(r, n);
dprintf("upd765: read %s: mt=%u, sk=%u, hd=%u, us=%u, c=%u, h=%u, r=%u, n=%u, eot=%u, dtl=%u, pc=%u, ph=%u\n",
CmdStr, mt, sk, ph[us], us, c, h, r, n, eot, Data[8], pc[us], ph[us]);
if(SecHdr != nullptr)
{
pr[us] = u8(SecHdr - SelDrive->t.hdr);
DataLen = SecHdr->datlen;
MaxDataLen = unsigned(MAX_TRACK_LEN - (SecHdr->data - SelDrive->t.trkd));
}
Msr |= MSR_EXM;
St0 = u8((ph[us] << 2U) | us);
St1 = 0;
St2 = 0;
if(SecHdr == nullptr || SecHdr->data == nullptr)
{
Msr &= ~MSR_EXM;
if(ReadData)
{
St0 |= ST0_AT1;
}
St1 |= ST1_ND;
DataLen = 0;
}
if(SecHdr != nullptr && SecHdr->c1 == 0)
{
Msr &= ~MSR_EXM;
St0 |= ST0_AT1;
St1 |= ST1_DE;
DataLen = 0;
}
if(SecHdr != nullptr && (Cmd == 0b00110 && (SecHdr->Flags & SECHDR::FL_DDAM) ||
Cmd == 0b01100 && !(SecHdr->Flags & SECHDR::FL_DDAM)))
{
St2 |= ST2_CM;
if(sk != 0) // skip data/deleted data
{
Msr &= ~MSR_EXM;
DataLen = 0;
}
}
if(SecHdr != nullptr && SecHdr->c != c)
{
St2 |= ST2_WC;
if(c == 0xFF)
{
St2 |= ST2_BC;
}
}
Result[0] = &St0;
Result[1] = &St1;
Result[2] = &St2;
Result[3] = &c;
Result[4] = &h;
Result[5] = &r;
Result[6] = &n;
ResultLen = 7;
Msr |= MSR_RQM | MSR_DIO; // CPU<-FDC
break;
}
case 0b00111: // recalibrate
{
auto us{ Data[1] & 3U };
SelDrive = &comp.fdd[us];
trdos_seek = ROMLED_TIME;
dprintf("upd765: recalibrate: us=%u\n", us);
pc[us] = 0;
ph[us] = 0;
pr[us] = 0;
seek();
St0 = u8(ST0_SE | us);
St3 |= ST3_T0;
Msr &= ~(MSR_DIO | MSR_CB); // CPU->FDC
break;
}
case 0b01000: // sense int
{
Result[0] = &St0;
Result[1] = &pc[SelDrive->Id];
ResultLen = 2;
Msr |= MSR_RQM | MSR_DIO; // CPU<-FDC
dprintf("upd765: sense int: st0=0x%02X, pc=%u, c=%u\n", St0, pc[SelDrive->Id], c);
break;
}
case 0b01010: // read id
{
u8 us{ u8(Data[1] & 1U) };
ph[us] = (Data[1] >> 2U) & 1U;
SelDrive = &comp.fdd[us];
load();
St0 = u8((ph[us] << 2U) | us);
St1 = 0;
St2 = 0;
const auto opr = pr[us];
if(SelDrive->t.s == 0) // ╥Ё¤ъ схч ёхъЄюЁют
{
St1 |= ST1_MA;
}
else
{
const auto &SecHdr = SelDrive->t.hdr[pr[us]];
pr[us]++;
if(pr[us] >= SelDrive->t.s)
{
pr[us] = 0;
}
c = SecHdr.c;
h = SecHdr.s;
r = SecHdr.n;
n = SecHdr.l;
if(SecHdr.c1 == 0)
{
St1 |= ST1_DE | ST1_ND;
}
if(c == 0xFF && c != SecHdr.c)
{
St2 |= ST2_BC;
}
}
Result[0] = &St0;
Result[1] = &St1;
Result[2] = &St2;
Result[3] = &c;
Result[4] = &h;
Result[5] = &r;
Result[6] = &n;
ResultLen = 7;
Msr |= MSR_RQM | MSR_DIO; // CPU<-FDC
dprintf("upd765: read id: hd=%u, us=%u, c=%u, h=%u, r=%u, n=%u, pc=%u, ph=%u, pr=%u\n",
h, Data[1] & 3U, c, h, r, n, pc[us], ph[us], opr);
break;
}
case 0b01111: // seek
{
trdos_seek = ROMLED_TIME;
u8 us{ u8(Data[1] & 3U) };
auto oph = ph[us];
auto opc = pc[us];
ph[us] = (Data[1] >> 2U) & 1U;
pc[us] = u8(Data[2]);
SelDrive = &comp.fdd[us];
if(oph != ph[us] || opc != pc[us])
{
pr[us] = 0;
}
dprintf("upd765: seek: hd=%u, us=%u, ncn=%u\n", ph[us], us, pc[us]);
seek();
St0 = u8(ST0_SE | (ph[us] << 2U) | us);
if(pc[us] != 0)
{
St3 &= ~ST3_T0;
}
Msr &= ~(MSR_DIO | MSR_CB); // CPU->FDC
break;
}
default:
dprintf("upd765: unk cmd=0x%02X\n", Data[0]);
St0 = ST0_IC;
Result[0] = &St0;
ResultLen = 1;
Msr |= MSR_RQM | MSR_DIO; // CPU<-FDC
break;
}
DataPtr = 0;
}
}
static unsigned PollCnt = 10;
static unsigned DataLenPrev = 0;
u8 TUpd765::in(u8 port)
{
switch(port)
{
case 2: // 2FFD (main status register)
// printf("upd765: read msr: msr=0x%02X\n", Msr);
if(DataLen != 0)
{
if(PollCnt == 0)
{
ReadDataReg();
St1 |= ST1_OR;
}
else
{
if(DataLen == DataLenPrev)
{
PollCnt--;
}
}
DataLenPrev = DataLen;
}
else
{
PollCnt = 10;
}
return Msr; // фхЄхъЄ +3/+2A юёє∙хёЄты хЄё яю сшЄє D7 main status register (хёыш 1, Єю +3)
case 3: // 3FFD (ЁхушёЄЁ фрээ√ї)
return ReadDataReg();
}
return 0xFF;
}
u8 TUpd765::ReadDataReg()
{
if(DataLen != 0) // ─рээ√х
{
trdos_load = ROMLED_TIME;
const auto *SecHdr = &SelDrive->t.hdr[pr[SelDrive->Id]];//SelDrive->t.get_sector(r, n);
auto DataIdx = SecHdr->datlen - DataLen;
auto Data = SecHdr->data[DataIdx];
DataLen--;
MaxDataLen--;
bool Overrun = false;
if(MaxDataLen == 0)
{
Overrun = (DataLen != 0);
DataLen = 0;
}
if(DataLen == 0)
{
pr[SelDrive->Id]++;// = u8(SecHdr - SelDrive->t.hdr + 1);
pr[SelDrive->Id] %= SelDrive->t.s;
}
if(DataLen == 0 && SecHdr->c2 == 0)
{
Msr &= ~MSR_EXM;
St0 &= ~ST0_IC_MASK;
St0 |= ST0_AT1;
St1 |= ST1_DE;
St2 |= ST2_DD;
Data ^= rdtsc(); // ╬°шсър crc т чюэх фрээ√ї ёхъЄюЁр
dprintf("upd765: data transfer complete1\n");
return Data;
}
if(DataLen == 0 && r < eot)
{
r++;
SecHdr = SelDrive->t.get_sector(r, n);
if(SecHdr == nullptr) // ╤хъЄюЁ эх эрщфхэ
{
St0 &= ~ST0_IC_MASK;
St0 |= ST0_AT1;
St1 |= ST1_ND;
St1 |= ST1_MA;
St2 |= ST2_MD;
DataLen = 0;
}
else
{
pr[SelDrive->Id] = u8(SecHdr - SelDrive->t.hdr);
pr[SelDrive->Id] %= SelDrive->t.s;
n = SecHdr->l;
DataLen = SecHdr->datlen;
if(SecHdr->c1 == 0)
{
St0 &= ~ST0_IC_MASK;
St0 |= ST0_AT1;
St1 |= ST1_DE;
St2 |= ST2_DD;
DataLen = 0;
}
}
}
if(ReadData && DataLen == 0 && (r >= eot || Overrun))
{
St0 &= ~ST0_IC_MASK;
St0 |= ST0_AT1;
St1 |= ST1_EN; // ┬ёхуфр єёЄрэютыхэ эр +3 (ёь. ъюььхэЄрЁшш т фшчрёёхьсыхЁх +3dos (0x204A))
}
if(DataLen == 0)
{
dprintf("upd765: data transfer complete2\n");
Msr &= ~MSR_EXM;
}
return Data;
}
else // ╨хчєы№ЄрЄ√
{
assert((Msr & MSR_EXM) == 0);
if(ResultPtr < ResultLen)
{
u8 Res = *Result[ResultPtr++];
dprintf("upd765: read result: 0x%02X\n", Res);
if(ResultPtr == ResultLen)
{
dprintf("upd765: read result complete\n");
assert((Msr & (MSR_DIO | MSR_CB)) != 0);
Msr &= ~(MSR_DIO | MSR_CB); // CPU->FDC
St0 &= ~ST0_IC_MASK;
St0 |= ST0_IC;
}
return Res;
}
else
{
dprintf("upd765: error no result to read\n");
}
}
return 0xFF;
}
void TUpd765::load()
{
seek(LOAD_SECTORS);
}
void TUpd765::seek(SEEK_MODE SeekMode)
{
auto cyl{ pc[SelDrive->Id] /*/ 2*/ };
if(cyl > 42)
{
cyl = 42;
}
SelDrive->track = cyl;
SelDrive->t.seek(SelDrive, cyl, ph[SelDrive->Id], SeekMode);
}
TUpd765 Upd765;