SM(S)C FDC37C669 Super I/O chip rewrite and proper FDC power down behavior, fixes floppy drive errors on the new Daeweoo machine.

This commit is contained in:
OBattler
2024-01-07 01:42:34 +01:00
parent 2fb04b63ec
commit 4f392ca8e3
5 changed files with 397 additions and 340 deletions

View File

@@ -212,6 +212,7 @@ fdc_ctrl_reset(void *priv)
fdc->lock = 0;
fdc->head = 0;
fdc->step = 0;
fdc->power_down = 0;
if (!(fdc->flags & FDC_FLAG_AT))
fdc->rate = 2;
}
@@ -257,7 +258,7 @@ fdc_set_wrong_am(fdc_t *fdc)
int
fdc_get_drive(fdc_t *fdc)
{
return fdc->drive;
return (int) fdc->drive;
}
int fdc_get_bitcell_period(fdc_t *fdc);
@@ -270,7 +271,7 @@ fdc_get_perp(fdc_t *fdc)
if (!(fdc->flags & FDC_FLAG_AT) || (fdc->flags & FDC_FLAG_PCJR))
return 0;
return fdc->perp;
return (int) fdc->perp;
}
int
@@ -292,7 +293,7 @@ fdc_get_gap2(fdc_t *fdc, int drive)
int
fdc_get_format_n(fdc_t *fdc)
{
return fdc->format_n;
return (int) fdc->format_n;
}
int
@@ -321,7 +322,7 @@ fdc_stop_id_request(fdc_t *fdc)
int
fdc_get_gap(fdc_t *fdc)
{
return fdc->gap;
return (int) fdc->gap;
}
int
@@ -333,7 +334,7 @@ fdc_get_dtl(fdc_t *fdc)
int
fdc_get_format_sectors(fdc_t *fdc)
{
return fdc->format_sectors;
return (int) fdc->format_sectors;
}
static void
@@ -418,6 +419,12 @@ fdc_update_rates(fdc_t *fdc)
fdc_rate(fdc, 3);
}
void
fdc_set_power_down(fdc_t *fdc, uint8_t power_down)
{
fdc->power_down = power_down;
}
void
fdc_update_max_track(fdc_t *fdc, int max_track)
{
@@ -427,7 +434,7 @@ fdc_update_max_track(fdc_t *fdc, int max_track)
void
fdc_update_enh_mode(fdc_t *fdc, int enh_mode)
{
fdc->enh_mode = enh_mode;
fdc->enh_mode = !!enh_mode;
fdc_update_rates(fdc);
}
@@ -490,7 +497,7 @@ fdc_update_drvrate(fdc_t *fdc, int drive, int drvrate)
void
fdc_update_drv2en(fdc_t *fdc, int drv2en)
{
fdc->drv2en = drv2en;
fdc->drv2en = !!drv2en;
}
void
@@ -500,37 +507,34 @@ fdc_update_rate(fdc_t *fdc, int drive)
fdc->bit_rate = 500;
else if ((fdc->rwc[drive] == 3) && fdc->enh_mode)
fdc->bit_rate = 250;
else
switch (fdc->rate) {
case 0: /*High density*/
fdc->bit_rate = 500;
break;
case 1: /*Double density (360 rpm)*/
switch (fdc->drvrate[drive]) {
case 0:
fdc->bit_rate = 300;
break;
case 1:
fdc->bit_rate = 500;
break;
case 2:
fdc->bit_rate = 2000;
break;
default:
break;
}
break;
case 2: /*Double density*/
fdc->bit_rate = 250;
break;
case 3: /*Extended density*/
fdc->bit_rate = 1000;
break;
default:
break;
}
else switch (fdc->rate) {
default:
break;
case 0: /*High density*/
fdc->bit_rate = 500;
break;
case 1: /*Double density (360 rpm)*/
switch (fdc->drvrate[drive]) {
default:
break;
case 0:
fdc->bit_rate = 300;
break;
case 1:
fdc->bit_rate = 500;
break;
case 2:
fdc->bit_rate = 2000;
break;
}
break;
case 2: /*Double density*/
fdc->bit_rate = 250;
break;
case 3: /*Extended density*/
fdc->bit_rate = 1000;
break;
}
fdc->bitcell_period = (1000000 / fdc->bit_rate) * 2; /*Bitcell period in ns*/
}
@@ -688,10 +692,6 @@ fdc_io_command_phase1(fdc_t *fdc, int out)
fdc->stat |= 0x20;
else
dma_set_drq(fdc->dma_ch, 1);
if (out)
fdc->pos = 0;
else
fdc->inread = 1;
}
static void
@@ -741,7 +741,7 @@ fdc_write(uint16_t addr, uint8_t val, void *priv)
cycles -= ISA_CYCLES(8);
switch (addr & 7) {
if (!fdc->power_down || ((addr & 7) == 2) || ((addr & 7) == 4)) switch (addr & 7) {
case 0:
return;
case 1:
@@ -776,14 +776,20 @@ fdc_write(uint16_t addr, uint8_t val, void *priv)
fdc->pnum = fdc->ptot = 0;
}
if ((val & 4) && !(fdc->dor & 4)) {
timer_set_delay_u64(&fdc->timer, 8 * TIMER_USEC);
fdc->interrupt = -1;
fdc->perp &= 0xfc;
if (fdc->power_down) {
timer_set_delay_u64(&fdc->timer, 1000 * TIMER_USEC);
fdc->interrupt = -5;
} else {
timer_set_delay_u64(&fdc->timer, 8 * TIMER_USEC);
fdc->interrupt = -1;
for (i = 0; i < FDD_NUM; i++)
ui_sb_update_icon(SB_FLOPPY | i, 0);
fdc->perp &= 0xfc;
fdc_ctrl_reset(fdc);
for (i = 0; i < FDD_NUM; i++)
ui_sb_update_icon(SB_FLOPPY | i, 0);
fdc_ctrl_reset(fdc);
}
}
/* We can now simplify this since each motor now spins separately. */
for (i = 0; i < FDD_NUM; i++) {
@@ -854,7 +860,6 @@ fdc_write(uint16_t addr, uint8_t val, void *priv)
fdc->pnum = 0;
fdc->ptot = 4;
fdc->stat |= 0x90;
fdc->pos = 0;
fdc->format_state = 0;
} else
fdc_bad_command(fdc);
@@ -866,7 +871,6 @@ fdc_write(uint16_t addr, uint8_t val, void *priv)
fdc->pnum = 0;
fdc->ptot = 8;
fdc->stat |= 0x90;
fdc->pos = 0;
fdc->mfm = (fdc->command & 0x40) ? 1 : 0;
break;
case 0x03: /*Specify*/
@@ -888,7 +892,6 @@ fdc_write(uint16_t addr, uint8_t val, void *priv)
fdc->pnum = 0;
fdc->ptot = 8;
fdc->stat |= 0x90;
fdc->pos = 0;
fdc->mfm = (fdc->command & 0x40) ? 1 : 0;
break;
case 0x06: /*Read data*/
@@ -907,7 +910,6 @@ fdc_write(uint16_t addr, uint8_t val, void *priv)
fdc->pnum = 0;
fdc->ptot = 8;
fdc->stat |= 0x90;
fdc->pos = 0;
fdc->mfm = (fdc->command & 0x40) ? 1 : 0;
break;
case 0x17: /*Powerdown mode*/
@@ -924,28 +926,24 @@ fdc_write(uint16_t addr, uint8_t val, void *priv)
case 0x08: /*Sense interrupt status*/
fdc_log("fdc->fintr = %i, fdc->reset_stat = %i\n", fdc->fintr, fdc->reset_stat);
fdc->lastdrive = fdc->drive;
fdc->pos = 0;
fdc_sis(fdc);
break;
case 0x0a: /*Read sector ID*/
fdc->pnum = 0;
fdc->ptot = 1;
fdc->stat |= 0x90;
fdc->pos = 0;
fdc->mfm = (fdc->command & 0x40) ? 1 : 0;
break;
case 0x0d: /*Format track*/
fdc->pnum = 0;
fdc->ptot = 5;
fdc->stat |= 0x90;
fdc->pos = 0;
fdc->mfm = (fdc->command & 0x40) ? 1 : 0;
fdc->format_state = 0;
break;
case 0x0e: /*Dump registers*/
fdc->lastdrive = fdc->drive;
fdc->interrupt = 0x0e;
fdc->pos = 0;
fdc_callback(fdc);
break;
case 0x0f: /*Seek*/
@@ -964,7 +962,6 @@ fdc_write(uint16_t addr, uint8_t val, void *priv)
case 0x94: /*Lock*/
fdc->lastdrive = fdc->drive;
fdc->interrupt = fdc->command;
fdc->pos = 0;
fdc_callback(fdc);
break;
case 0x12: /*Set perpendicular mode*/
@@ -972,7 +969,6 @@ fdc_write(uint16_t addr, uint8_t val, void *priv)
fdc->pnum = 0;
fdc->ptot = 1;
fdc->stat |= 0x90;
fdc->pos = 0;
} else
fdc_bad_command(fdc);
break;
@@ -980,7 +976,6 @@ fdc_write(uint16_t addr, uint8_t val, void *priv)
fdc->pnum = 0;
fdc->ptot = 3;
fdc->stat |= 0x90;
fdc->pos = 0;
break;
default:
fdc_bad_command(fdc);
@@ -1151,7 +1146,6 @@ fdc_write(uint16_t addr, uint8_t val, void *priv)
fdc->format_sectors = fdc->params[2];
fdc->format_n = fdc->params[1];
fdc->format_state = 1;
fdc->pos = 0;
fdc->stat = 0x10;
break;
case 0x0f: /* Seek */
@@ -1273,12 +1267,12 @@ uint8_t
fdc_read(uint16_t addr, void *priv)
{
fdc_t *fdc = (fdc_t *) priv;
uint8_t ret;
uint8_t ret = 0xff;
int drive = 0;
cycles -= ISA_CYCLES(8);
switch (addr & 7) {
if (!fdc->power_down || ((addr & 7) == 2)) switch (addr & 7) {
case 0: /* STA */
if (fdc->flags & FDC_FLAG_PS1) {
drive = real_drive(fdc, fdc->dor & 3);
@@ -1513,7 +1507,6 @@ fdc_poll_readwrite_finish(fdc_t *fdc, int compare)
if ((fdc->interrupt == 5) || (fdc->interrupt == 9))
fdd_do_writeback(real_drive(fdc, fdc->drive));
fdc->inread = 0;
fdc->interrupt = -2;
fdc_poll_common_finish(fdc, compare, 0);
@@ -1544,10 +1537,21 @@ fdc_callback(void *priv)
case -2: /*End of command*/
fdc->stat = (fdc->stat & 0xf) | 0x80;
return;
case -5: /*Reset in power down mode */
fdc->perp &= 0xfc;
for (uint8_t i = 0; i < FDD_NUM; i++)
ui_sb_update_icon(SB_FLOPPY | i, 0);
fdc_ctrl_reset(fdc);
fdc->fintr = 0;
memset(fdc->pcn, 0x00, 4 * sizeof(uint16_t));
return;
case -1: /*Reset*/
fdc_int(fdc, 1);
fdc->fintr = 0;
memset(fdc->pcn, 0, 4 * sizeof(int));
memset(fdc->pcn, 0x00, 4 * sizeof(uint16_t));
fdc->reset_stat = 4;
return;
case 0x01: /* Mode */
@@ -1570,7 +1574,6 @@ fdc_callback(void *priv)
fdc->stat = 0x50;
}
}
fdc->inread = 1;
return;
case 0x04: /* Sense drive status */
fdc->res[10] = (fdc->params[0] & 7) | 0x20;
@@ -1715,7 +1718,6 @@ fdc_callback(void *priv)
default:
break;
}
fdc->inread = 1;
return;
case 0x07: /* Recalibrate */
fdc->pcn[fdc->params[0] & 3] = 0;
@@ -2004,18 +2006,11 @@ fdc_data(fdc_t *fdc, uint8_t data, int last)
return 0;
}
void
fdc_finishread(fdc_t *fdc)
{
fdc->inread = 0;
}
void
fdc_track_finishread(fdc_t *fdc, int condition)
{
fdc->stat = 0x10;
fdc->satisfying_sectors |= condition;
fdc->inread = 0;
fdc_callback(fdc);
}
@@ -2025,7 +2020,6 @@ fdc_sector_finishcompare(fdc_t *fdc, int satisfying)
fdc->stat = 0x10;
if (satisfying)
fdc->satisfying_sectors++;
fdc->inread = 0;
fdc_callback(fdc);
}
@@ -2033,7 +2027,6 @@ void
fdc_sector_finishread(fdc_t *fdc)
{
fdc->stat = 0x10;
fdc->inread = 0;
fdc_callback(fdc);
}
@@ -2356,6 +2349,8 @@ fdc_reset(void *priv)
for (uint8_t i = 0; i < FDD_NUM; i++)
ui_sb_update_icon(SB_FLOPPY | i, 0);
fdc->power_down = 0;
}
static void
@@ -2418,7 +2413,7 @@ fdc_init(const device_t *info)
void
fdc_3f1_enable(fdc_t *fdc, int enable)
{
fdc->enable_3f1 = enable;
fdc->enable_3f1 = !!enable;
}
const device_t fdc_xt_device = {

View File

@@ -1274,18 +1274,19 @@ d86f_find_address_mark_fm(int drive, int side, find_t *find, uint16_t req_am, ui
if (dev->last_word[side] == req_am) {
dev->calc_crc.word = 0xFFFF;
fdd_calccrc(decodefm(drive, dev->last_word[side]), &(dev->calc_crc));
find->sync_marks = find->bits_obtained = find->bytes_obtained = 0;
find->sync_pos = 0xFFFFFFFF;
dev->preceding_bit[side] = dev->last_word[side] & 1;
find->sync_marks = find->bits_obtained =
find->bytes_obtained = 0;
find->sync_pos = 0xFFFFFFFF;
dev->preceding_bit[side] = dev->last_word[side] & 1;
dev->state++;
return;
}
if (wrong_am && (dev->last_word[side] == wrong_am)) {
dev->data_find.sync_marks = dev->data_find.bits_obtained = dev->data_find.bytes_obtained = 0;
dev->error_condition = 0;
dev->state = STATE_IDLE;
fdc_finishread(d86f_fdc);
dev->data_find.sync_marks = dev->data_find.bits_obtained =
dev->data_find.bytes_obtained = 0;
dev->error_condition = 0;
dev->state = STATE_IDLE;
fdc_nodataam(d86f_fdc);
return;
}
@@ -1328,8 +1329,9 @@ d86f_write_find_address_mark_fm(int drive, int side, find_t *find)
/* If we hadn't found enough set bits but have found a clear bit, null the counter of set bits. */
if (!(dev->last_word[side] & 1)) {
find->sync_marks = find->bits_obtained = find->bytes_obtained = 0;
find->sync_pos = 0xFFFFFFFF;
find->sync_marks = find->bits_obtained =
find->bytes_obtained = 0;
find->sync_pos = 0xFFFFFFFF;
}
}
@@ -1347,10 +1349,10 @@ d86f_find_address_mark_mfm(int drive, int side, find_t *find, uint16_t req_am, u
}
if (wrong_am && (dev->last_word[side] == wrong_am) && (find->sync_marks >= 3)) {
dev->data_find.sync_marks = dev->data_find.bits_obtained = dev->data_find.bytes_obtained = 0;
dev->error_condition = 0;
dev->state = STATE_IDLE;
fdc_finishread(d86f_fdc);
dev->data_find.sync_marks = dev->data_find.bits_obtained =
dev->data_find.bytes_obtained = 0;
dev->error_condition = 0;
dev->state = STATE_IDLE;
fdc_nodataam(d86f_fdc);
return;
}
@@ -1433,22 +1435,26 @@ d86f_read_sector_id(int drive, int side, int match)
if (!(dev->id_find.bits_obtained & 15)) {
/* We've got a byte. */
if (dev->id_find.bytes_obtained < 4) {
dev->last_sector.byte_array[dev->id_find.bytes_obtained] = decodefm(drive, dev->last_word[side]);
dev->last_sector.byte_array[dev->id_find.bytes_obtained] =
decodefm(drive, dev->last_word[side]);
fdd_calccrc(dev->last_sector.byte_array[dev->id_find.bytes_obtained], &(dev->calc_crc));
} else if ((dev->id_find.bytes_obtained >= 4) && (dev->id_find.bytes_obtained < 6)) {
dev->track_crc.bytes[(dev->id_find.bytes_obtained & 1) ^ 1] = decodefm(drive, dev->last_word[side]);
dev->track_crc.bytes[(dev->id_find.bytes_obtained & 1) ^ 1] =
decodefm(drive, dev->last_word[side]);
}
dev->id_find.bytes_obtained++;
if (dev->id_find.bytes_obtained == 6) {
/* We've got the ID. */
if ((dev->calc_crc.word != dev->track_crc.word) && (dev->last_sector.dword == dev->req_sector.dword)) {
dev->id_find.sync_marks = dev->id_find.bits_obtained = dev->id_find.bytes_obtained = 0;
d86f_log("86F: ID CRC error: %04X != %04X (%08X)\n", dev->track_crc.word, dev->calc_crc.word, dev->last_sector.dword);
if ((dev->calc_crc.word != dev->track_crc.word) &&
(dev->last_sector.dword == dev->req_sector.dword)) {
dev->id_find.sync_marks = dev->id_find.bits_obtained =
dev->id_find.bytes_obtained = 0;
d86f_log("86F: ID CRC error: %04X != %04X (%08X)\n", dev->track_crc.word,
dev->calc_crc.word, dev->last_sector.dword);
if ((dev->state != STATE_02_READ_ID) && (dev->state != STATE_0A_READ_ID)) {
dev->error_condition = 0;
dev->state = STATE_IDLE;
fdc_finishread(d86f_fdc);
fdc_headercrcerror(d86f_fdc);
} else if (dev->state == STATE_0A_READ_ID)
dev->state--;
@@ -1458,25 +1464,37 @@ d86f_read_sector_id(int drive, int side, int match)
}
} else if ((dev->calc_crc.word == dev->track_crc.word) && (dev->state == STATE_0A_READ_ID)) {
/* CRC is valid and this is a read sector ID command. */
dev->id_find.sync_marks = dev->id_find.bits_obtained = dev->id_find.bytes_obtained = dev->error_condition = 0;
fdc_sectorid(d86f_fdc, dev->last_sector.id.c, dev->last_sector.id.h, dev->last_sector.id.r, dev->last_sector.id.n, 0, 0);
dev->id_find.sync_marks = dev->id_find.bits_obtained =
dev->id_find.bytes_obtained = dev->error_condition = 0;
fdc_sectorid(d86f_fdc,
dev->last_sector.id.c, dev->last_sector.id.h,
dev->last_sector.id.r, dev->last_sector.id.n, 0, 0);
dev->state = STATE_IDLE;
} else {
/* CRC is valid. */
dev->id_find.sync_marks = dev->id_find.bits_obtained = dev->id_find.bytes_obtained = 0;
dev->id_find.sync_marks = dev->id_find.bits_obtained =
dev->id_find.bytes_obtained = 0;
dev->id_found |= 1;
if ((dev->last_sector.dword == dev->req_sector.dword) || !match) {
d86f_handler[drive].set_sector(drive, side, dev->last_sector.id.c, dev->last_sector.id.h, dev->last_sector.id.r, dev->last_sector.id.n);
d86f_handler[drive].set_sector(drive, side,
dev->last_sector.id.c, dev->last_sector.id.h,
dev->last_sector.id.r, dev->last_sector.id.n);
if (dev->state == STATE_02_READ_ID) {
/* READ TRACK command, we need some special handling here. */
/* Code corrected: Only the C, H, and N portions of the sector ID are compared, the R portion (the sector number) is ignored. */
if ((dev->last_sector.id.c != fdc_get_read_track_sector(d86f_fdc).id.c) || (dev->last_sector.id.h != fdc_get_read_track_sector(d86f_fdc).id.h) || (dev->last_sector.id.n != fdc_get_read_track_sector(d86f_fdc).id.n)) {
dev->error_condition |= 4; /* Mark that the sector ID is not the one expected by the FDC. */
/* Code corrected: Only the C, H, and N portions of the
sector ID are compared, the R portion
(the sector number) is ignored. */
if ((dev->last_sector.id.c != fdc_get_read_track_sector(d86f_fdc).id.c) ||
(dev->last_sector.id.h != fdc_get_read_track_sector(d86f_fdc).id.h) ||
(dev->last_sector.id.n != fdc_get_read_track_sector(d86f_fdc).id.n)) {
/* Mark that the sector ID is not the one expected by the FDC. */
dev->error_condition |= 4;
/* Make sure we use the sector size from the FDC. */
dev->last_sector.id.n = fdc_get_read_track_sector(d86f_fdc).id.n;
}
/* If the two ID's are identical, then we do not need to do anything regarding the sector size. */
/* If the two ID's are identical, then we do not need to do
anything regarding the sector size. */
}
dev->state++;
} else {
@@ -1576,7 +1594,8 @@ d86f_read_sector_data(int drive, int side)
data = d86f_handler[drive].read_data(drive, side, dev->data_find.bytes_obtained);
else {
#ifdef HACK_FOR_DBASE_III
if ((dev->last_sector.id.c == 39) && (dev->last_sector.id.h == 0) && (dev->last_sector.id.r == 5) && (dev->data_find.bytes_obtained >= 272))
if ((dev->last_sector.id.c == 39) && (dev->last_sector.id.h == 0) &&
(dev->last_sector.id.r == 5) && (dev->data_find.bytes_obtained >= 272))
data = (random_generate() & 0xff);
else
#endif
@@ -1589,7 +1608,9 @@ d86f_read_sector_data(int drive, int side)
} else {
if (dev->data_find.bytes_obtained < d86f_get_data_len(drive)) {
if (dev->state != STATE_16_VERIFY_DATA) {
read_status = fdc_data(d86f_fdc, data, dev->data_find.bytes_obtained == (d86f_get_data_len(drive) - 1));
read_status = fdc_data(d86f_fdc, data,
dev->data_find.bytes_obtained ==
(d86f_get_data_len(drive) - 1));
if (read_status == -1)
dev->dma_over++;
}
@@ -1597,17 +1618,19 @@ d86f_read_sector_data(int drive, int side)
}
fdd_calccrc(data, &(dev->calc_crc));
} else if (dev->data_find.bytes_obtained < crc_pos)
dev->track_crc.bytes[(dev->data_find.bytes_obtained - sector_len) ^ 1] = decodefm(drive, dev->last_word[side]);
dev->track_crc.bytes[(dev->data_find.bytes_obtained - sector_len) ^ 1] =
decodefm(drive, dev->last_word[side]);
dev->data_find.bytes_obtained++;
if (dev->data_find.bytes_obtained == (crc_pos + fdc_get_gap(d86f_fdc))) {
/* We've got the data. */
if ((dev->calc_crc.word != dev->track_crc.word) && (dev->state != STATE_02_READ_DATA)) {
d86f_log("86F: Data CRC error: %04X != %04X (%08X)\n", dev->track_crc.word, dev->calc_crc.word, dev->last_sector.dword);
dev->data_find.sync_marks = dev->data_find.bits_obtained = dev->data_find.bytes_obtained = 0;
dev->error_condition = 0;
dev->state = STATE_IDLE;
fdc_finishread(d86f_fdc);
d86f_log("86F: Data CRC error: %04X != %04X (%08X)\n", dev->track_crc.word,
dev->calc_crc.word, dev->last_sector.dword);
dev->data_find.sync_marks = dev->data_find.bits_obtained =
dev->data_find.bytes_obtained = 0;
dev->error_condition = 0;
dev->state = STATE_IDLE;
fdc_datacrcerror(d86f_fdc);
} else if ((dev->calc_crc.word != dev->track_crc.word) && (dev->state == STATE_02_READ_DATA)) {
dev->data_find.sync_marks = dev->data_find.bits_obtained = dev->data_find.bytes_obtained = 0;
@@ -2146,7 +2169,8 @@ d86f_turbo_read(int drive, int side)
} else {
if (dev->turbo_pos < (128UL << dev->req_sector.id.n)) {
if (dev->state != STATE_16_VERIFY_DATA) {
read_status = fdc_data(d86f_fdc, dat, dev->turbo_pos == ((128UL << dev->req_sector.id.n) - 1));
read_status = fdc_data(d86f_fdc, dat,
dev->turbo_pos == ((128UL << dev->req_sector.id.n) - 1));
if (read_status == -1)
dev->dma_over++;
}
@@ -2163,7 +2187,6 @@ d86f_turbo_read(int drive, int side)
#endif
dev->error_condition = 0;
dev->state = STATE_IDLE;
fdc_finishread(d86f_fdc);
fdc_datacrcerror(d86f_fdc);
} else if ((flags & SECTOR_CRC_ERROR) && (dev->state == STATE_02_READ_DATA)) {
#ifdef ENABLE_D86F_LOG