diff --git a/.vscode/launch.json b/.vscode/launch.json index e9587de..0ff7549 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -2,28 +2,55 @@ "version": "0.2.0", "configurations": [ { - "name": "(lldb) Launch macOS", - "type": "cppdbg", + "name": "macOS: 通常起動 (lldb extension)", + "type": "lldb", "request": "launch", "program": "${workspaceFolder}/build/XM8.app/Contents/MacOS/xm8", "args": [], - "stopAtEntry": false, - "cwd": "${workspaceFolder}", - "environment": [], - "externalConsole": true, - "MIMode": "lldb" + "cwd": "${workspaceFolder}" }, { - "name": "(gdb) Launch Linux", + "name": "Linux: 通常起動 (gdb)", "type": "cppdbg", "request": "launch", "program": "${workspaceFolder}/build/xm8", "args": [], "stopAtEntry": false, "cwd": "${workspaceFolder}", - "environment": [], "externalConsole": false, "MIMode": "gdb" + }, + { + "name": "macOS: Wizardry 安定化 (LOST=15000 + CONST_EXEC=1)", + "type": "lldb", + "request": "launch", + "program": "${workspaceFolder}/build/XM8.app/Contents/MacOS/xm8", + "args": [], + "cwd": "${workspaceFolder}", + "env": { + "XM8_FDC_TRACE_MAX_LINES": "400000", + "XM8_FDC_SEEK_SCALE": "1000", + "XM8_DISABLE_UNSTABLE_MASK": "1", + "XM8_FDC_LOST_USEC": "15000", + "XM8_FDC_CONST_EXEC_USEC": "1", + "XM8_FDC_TRACE_FILE": "xm8_fdc_trace_wiz_stable.log" + } + }, + { + "name": "macOS: FDCトレース基準 (scale=1000, no-unstable)", + "type": "lldb", + "request": "launch", + "program": "${workspaceFolder}/build/XM8.app/Contents/MacOS/xm8", + "args": [], + "cwd": "${workspaceFolder}", + "env": { + "XM8_FDC_TRACE": "1", + "XM8_FDC_TRACE_IO": "1", + "XM8_FDC_TRACE_MAX_LINES": "400000", + "XM8_FDC_SEEK_SCALE": "1000", + "XM8_DISABLE_UNSTABLE_MASK": "1", + "XM8_FDC_TRACE_FILE": "xm8_fdc_trace_s1000_nounstable.log" + } } ] -} \ No newline at end of file +} diff --git a/Source/ePC-8801MA/vm/disk.cpp b/Source/ePC-8801MA/vm/disk.cpp index db57803..6519640 100644 --- a/Source/ePC-8801MA/vm/disk.cpp +++ b/Source/ePC-8801MA/vm/disk.cpp @@ -7,7 +7,8 @@ [ d88 handler ] */ -#include "disk.h" +#include "disk.h" +#include // crc table static const uint16 crc_table[256] = { @@ -70,7 +71,19 @@ static const int secsize[8] = { 128, 256, 512, 1024, 2048, 4096, 8192, 16384 }; -static uint8 tmp_buffer[DISK_BUFFER_SIZE]; +static uint8 tmp_buffer[DISK_BUFFER_SIZE]; +static bool g_disable_unstable_checked = false; +static bool g_disable_unstable = false; + +static bool disable_unstable_mask() +{ + if(!g_disable_unstable_checked) { + const char* env = getenv("XM8_DISABLE_UNSTABLE_MASK"); + g_disable_unstable = (env != NULL && env[0] != '\0' && env[0] != '0'); + g_disable_unstable_checked = true; + } + return g_disable_unstable; +} typedef struct { int type; @@ -401,14 +414,29 @@ void DISK::close() file_size.d = 0; sector_size.sd = sector_num.sd = 0; sector = NULL; + unstable = NULL; + sector_mfm = drive_mfm; + addr_crc_error = false; + data_crc_error = false; + crc_error = false; track_mfm = drive_mfm; } -bool DISK::get_track(int trk, int side) -{ - sector_size.sd = sector_num.sd = 0; - invalid_format = false; - no_skew = true; +bool DISK::get_track(int trk, int side) +{ + sector_size.sd = sector_num.sd = 0; + invalid_format = false; + no_skew = true; + if(media_type == MEDIA_TYPE_2D && drive_type == DRIVE_TYPE_2DD) { + if(trk >= 0) { + if(trk & 1) { + return false; // unformat track on 2DD side mapping + } + trk >>= 1; + } + } else if(media_type == MEDIA_TYPE_2DD && drive_type == DRIVE_TYPE_2D) { + if(trk >= 0) trk <<= 1; + } // disk not inserted or invalid media type if(!(inserted && check_media_type())) { @@ -483,59 +511,82 @@ bool DISK::get_track(int trk, int side) } } - uint8* t = sector; - int total = sync_size + (am_size + 1); - - for(int i = 0; i < sector_num.sd; i++) { - data_size.read_2bytes_le_from(t + 14); - total += sync_size + (am_size + 1) + (4 + 2) + gap2_size + sync_size + (am_size + 1); - total += data_size.sd + 2; - if(t[2] != i + 1) { - no_skew = false; - } - t += data_size.sd + 0x10; - } - if(gap3_size == 0) { - gap3_size = (get_track_size() - total - gap0_size - gap1_size) / (sector_num.sd + 1); - } - gap4_size = get_track_size() - total - gap0_size - gap1_size - gap3_size * sector_num.sd; - - if(gap3_size < 8 || gap4_size < 8) { - gap0_size = gap1_size = gap3_size = (get_track_size() - total) / (2 + sector_num.sd + 1); - gap4_size = get_track_size() - total - gap0_size - gap1_size - gap3_size * sector_num.sd; - } - if(gap3_size < 8 || gap4_size < 8) { - gap0_size = gap1_size = gap3_size = gap4_size = 32; - invalid_format = true; - } - int preamble_size = gap0_size + sync_size + (am_size + 1) + gap1_size; - - t = sector; - total = preamble_size; + uint8* t = sector; + int total = 0, valid_sector_num = 0; + + for(int i = 0; i < sector_num.sd; i++) { + data_size.read_2bytes_le_from(t + 14); + sync_position[i] = total; // for invalid format case + total += sync_size + (am_size + 1) + (4 + 2) + gap2_size; + if(data_size.sd > 0) { + total += sync_size + (am_size + 1); + total += data_size.sd + 2; + valid_sector_num++; + } + if(t[2] != i + 1) { + no_skew = false; + } + t += data_size.sd + 0x10; + } + total += sync_size + (am_size + 1); // sync in preamble + if(gap3_size == 0) { + gap3_size = (get_track_size() - total - gap0_size - gap1_size) / (valid_sector_num + 1); + } + gap4_size = get_track_size() - total - gap0_size - gap1_size - gap3_size * valid_sector_num; + + if(gap3_size < 8 || gap4_size < 8) { + gap0_size = gap1_size = gap3_size = (get_track_size() - total) / (2 + valid_sector_num + 1); + gap4_size = get_track_size() - total - gap0_size - gap1_size - gap3_size * valid_sector_num; + } + if(gap3_size < 8 || gap4_size < 8) { + gap0_size = gap1_size = gap3_size = gap4_size = 8; + invalid_format = true; + } + int preamble_size = gap0_size + sync_size + (am_size + 1) + gap1_size; + if(invalid_format) { + total -= sync_size + (am_size + 1); + for(int i = 0; i < sector_num.sd; i++) { + sync_position[i] *= get_track_size() - preamble_size - gap4_size; + sync_position[i] /= total; + } + } + + t = sector; + total = preamble_size; sync_position[array_length(sync_position) - 1] = gap0_size; // sync position in preamble - for(int i = 0; i < sector_num.sd; i++) { - data_size.read_2bytes_le_from(t + 14); - if(invalid_format) { - total = preamble_size + (get_track_size() - preamble_size - gap4_size) * i / sector_num.sd; - } - sync_position[i] = total; - total += sync_size + (am_size + 1); - id_position[i] = total; - total += (4 + 2) + gap2_size + sync_size + (am_size + 1); - data_position[i] = total; - total += data_size.sd + 2 + gap3_size; - t += data_size.sd + 0x10; - } + for(int i = 0; i < sector_num.sd; i++) { + data_size.read_2bytes_le_from(t + 14); + if(invalid_format) { + total = preamble_size + sync_position[i]; + } + sync_position[i] = total; + total += sync_size; + total += am_size + 1; + id_position[i] = total; + total += (4 + 2) + gap2_size; + if(data_size.sd > 0) { + total += sync_size + (am_size + 1); + data_position[i] = total; + total += data_size.sd + 2; + total += gap3_size; + } else { + data_position[i] = total; // no data field + } + t += data_size.sd + 0x10; + } return true; } -bool DISK::make_track(int trk, int side) -{ - int track_size = get_track_size(); - - if(!get_track(trk, side)) { - // create a dummy track +bool DISK::make_track(int trk, int side) +{ + // get_track() already applies 2D/2DD logical<->physical track conversion. + // Converting here as well causes a double-conversion mismatch between + // make_track() generated data positions and get_sector() lookups. + int track_size = get_track_size(); + + if(!get_track(trk, side)) { + // create a dummy track for(int i = 0; i < track_size; i++) { track[i] = rand(); } @@ -570,58 +621,71 @@ bool DISK::make_track(int trk, int side) data_size.read_2bytes_le_from(t + 14); int p = sync_position[i]; - // sync - for(int j = 0; j < sync_size; j++) { - if(p < track_size) track[p++] = 0x00; - } - // am1 - for(int j = 0; j < am_size; j++) { - if(p < track_size) track[p++] = 0xa1; - } - if(p < track_size) track[p++] = 0xfe; - // id - if(p < track_size) track[p++] = t[0]; - if(p < track_size) track[p++] = t[1]; - if(p < track_size) track[p++] = t[2]; - if(p < track_size) track[p++] = t[3]; - uint16 crc = 0; - crc = (uint16)((crc << 8) ^ crc_table[(uint8)(crc >> 8) ^ t[0]]); - crc = (uint16)((crc << 8) ^ crc_table[(uint8)(crc >> 8) ^ t[1]]); - crc = (uint16)((crc << 8) ^ crc_table[(uint8)(crc >> 8) ^ t[2]]); - crc = (uint16)((crc << 8) ^ crc_table[(uint8)(crc >> 8) ^ t[3]]); - if(p < track_size) track[p++] = (crc >> 8) & 0xff; - if(p < track_size) track[p++] = (crc >> 0) & 0xff; - // gap2 - for(int j = 0; j < gap2_size; j++) { - if(p < track_size) track[p++] = gap_data; - } - // sync - for(int j = 0; j < sync_size; j++) { - if(p < track_size) track[p++] = 0x00; - } - // am2 - for(int j = 0; j < am_size; j++) { - if(p < track_size) track[p++] = 0xa1; - } - if(p < track_size) track[p++] = (t[7] != 0) ? 0xf8 : 0xfb; - // data - crc = 0; - for(int j = 0; j < data_size.sd; j++) { - if(p < track_size) track[p++] = t[0x10 + j]; - crc = (uint16)((crc << 8) ^ crc_table[(uint8)(crc >> 8) ^ t[0x10 + j]]); - } + // sync + for(int j = 0; j < sync_size; j++) { + if(p < track_size) track[p++] = 0x00; + } + // am1 + uint16 crc = 0xffff; + for(int j = 0; j < am_size; j++) { + if(p < track_size) track[p++] = 0xa1; + crc = (uint16)((crc << 8) ^ crc_table[(uint8)(crc >> 8) ^ 0xa1]); + } + if(p < track_size) track[p++] = 0xfe; + crc = (uint16)((crc << 8) ^ crc_table[(uint8)(crc >> 8) ^ 0xfe]); + // id + if(p < track_size) track[p++] = t[0]; + if(p < track_size) track[p++] = t[1]; + if(p < track_size) track[p++] = t[2]; + if(p < track_size) track[p++] = t[3]; + crc = (uint16)((crc << 8) ^ crc_table[(uint8)(crc >> 8) ^ t[0]]); + crc = (uint16)((crc << 8) ^ crc_table[(uint8)(crc >> 8) ^ t[1]]); + crc = (uint16)((crc << 8) ^ crc_table[(uint8)(crc >> 8) ^ t[2]]); + crc = (uint16)((crc << 8) ^ crc_table[(uint8)(crc >> 8) ^ t[3]]); if(p < track_size) track[p++] = (crc >> 8) & 0xff; if(p < track_size) track[p++] = (crc >> 0) & 0xff; - - t += data_size.sd + 0x10; - } + // gap2 + for(int j = 0; j < gap2_size; j++) { + if(p < track_size) track[p++] = gap_data; + } + // data field (may be absent) + if(data_size.sd > 0) { + // sync + for(int j = 0; j < sync_size; j++) { + if(p < track_size) track[p++] = 0x00; + } + // am2 + crc = 0xffff; + for(int j = 0; j < am_size; j++) { + if(p < track_size) track[p++] = 0xa1; + crc = (uint16)((crc << 8) ^ crc_table[(uint8)(crc >> 8) ^ 0xa1]); + } + uint8 am2 = (t[7] != 0) ? 0xf8 : 0xfb; + if(p < track_size) track[p++] = am2; + crc = (uint16)((crc << 8) ^ crc_table[(uint8)(crc >> 8) ^ am2]); + // data + uint8* u = get_unstable_sector(sector, i, t[0], t[1], t[2], t[3], data_size.sd); + for(int j = 0; j < data_size.sd; j++) { + if(p < track_size) { + uint8 mask = (u != NULL && !disable_unstable_mask()) ? u[j] : 0; + track[p++] = (t[0x10 + j] & ~mask) | (rand() & mask); + } + crc = (uint16)((crc << 8) ^ crc_table[(uint8)(crc >> 8) ^ t[0x10 + j]]); + } + if(p < track_size) track[p++] = (crc >> 8) & 0xff; + if(p < track_size) track[p++] = (crc >> 0) & 0xff; + } + + t += data_size.sd + 0x10; + } return true; } bool DISK::get_sector(int trk, int side, int index) { - sector_size.sd = sector_num.sd = 0; - sector = NULL; + sector_size.sd = sector_num.sd = 0; + sector = NULL; + unstable = NULL; // disk not inserted or invalid media type if(!(inserted && check_media_type())) { @@ -629,11 +693,21 @@ bool DISK::get_sector(int trk, int side, int index) } // search track - if(trk == -1 && side == -1) { - trk = cur_track; - side = cur_side; - } - int trkside = trk * 2 + (side & 1); + if(trk == -1 && side == -1) { + trk = cur_track; + side = cur_side; + } + if(media_type == MEDIA_TYPE_2D && drive_type == DRIVE_TYPE_2DD) { + if(trk >= 0) { + if(trk & 1) { + return false; + } + trk >>= 1; + } + } else if(media_type == MEDIA_TYPE_2DD && drive_type == DRIVE_TYPE_2D) { + if(trk >= 0) trk <<= 1; + } + int trkside = trk * 2 + (side & 1); if(!(0 <= trkside && trkside < 164)) { return false; } @@ -648,20 +722,113 @@ bool DISK::get_sector(int trk, int side, int index) uint8* t = buffer + offset.d; sector_num.read_2bytes_le_from(t + 4); - if(index >= sector_num.sd) { - return false; - } - - // skip sector - for(int i = 0; i < index; i++) { - pair data_size; - data_size.read_2bytes_le_from(t + 14); - t += data_size.sd + 0x10; - } + if(index >= sector_num.sd) { + return false; + } + + // skip sector + for(int i = 0; i < index; i++) { + pair data_size; + data_size.read_2bytes_le_from(t + 14); + t += data_size.sd + 0x10; + } + pair target_size; + target_size.read_2bytes_le_from(t + 14); + unstable = get_unstable_sector(buffer + offset.d, index, t[0], t[1], t[2], t[3], target_size.sd); set_sector_info(t); return true; } +int DISK::get_track_num(uint8* t) +{ + int position = (int)(t - buffer); + int result = -1; + pair offset; + + offset.read_4bytes_le_from(buffer + 0x1c); + if(position < offset.sd) { + int max_tracks = 164; + for(int track = 0; track < 164; track++) { + offset.read_4bytes_le_from(buffer + 0x20 + track * 4); + if(offset.sd != 0) { + if(offset.sd < 0x2b0) { + max_tracks = min((offset.sd - 0x20) >> 2, 164); + } + break; + } + } + for(int track = 0; track < max_tracks; track++) { + offset.read_4bytes_le_from(buffer + 0x20 + track * 4); + if(offset.sd != 0 && position >= offset.sd) { + if(position == offset.sd) { + result = track; + break; + } else { + result = max(track, result); + } + } + } + } + return result; +} + +uint8* DISK::get_unstable_sector(uint8* t, int index, uint8 c, uint8 h, uint8 r, uint8 n, int size) +{ + int track = get_track_num(t); + if(track < 0) { + return NULL; + } + uint8* end = buffer + sizeof(buffer); + pair offset, num, idx, data_size; + offset.read_4bytes_le_from(buffer + 0x20 + track * 4); + t = buffer + offset.d; + if(t < buffer || t + 0x10 > end) { + return NULL; + } + num.read_2bytes_le_from(t + 4); + if(num.sd <= 0 || num.sd > 256) { + return NULL; + } + + for(int i = 0; i < num.sd; i++) { + if(t + 0x10 > end) { + return NULL; + } + data_size.read_2bytes_le_from(t + 14); + if(data_size.sd < 0 || data_size.sd > 16384 || t + data_size.sd + 0x10 > end) { + return NULL; + } + t += data_size.sd + 0x10; + } + if(track == get_track_num(t)) { + if(t < buffer || t + 0x10 > end) { + return NULL; + } + num.read_2bytes_le_from(t + 4); + if(num.sd <= 0 || num.sd > 256) { + return NULL; + } + for(int i = 0; i < num.sd; i++) { + if(t + 0x10 > end) { + return NULL; + } + idx.read_2bytes_le_from(t + 9); + data_size.read_2bytes_le_from(t + 14); + if(data_size.sd < 0 || data_size.sd > 16384 || t + data_size.sd + 0x10 > end) { + return NULL; + } + if(idx.sd == index) { + if(t[0] == c && t[1] == h && t[2] == r && t[3] == n && data_size.sd == size) { + return t + 0x10; + } + return NULL; + } + t += data_size.sd + 0x10; + } + } + return NULL; +} + bool DISK::get_sector_info(int trk, int side, int index, uint8* c, uint8* h, uint8* r, uint8* n, bool* mfm, int* length) { if(!get_sector(trk, side, index)) { @@ -680,7 +847,7 @@ bool DISK::get_sector_info(int trk, int side, int index, uint8* c, uint8* h, uin *n = id[3]; } if(mfm != NULL) { - *mfm = (density == 0x00); + *mfm = sector_mfm; } if(length != NULL) { *length = sector_size.sd; @@ -690,32 +857,41 @@ bool DISK::get_sector_info(int trk, int side, int index, uint8* c, uint8* h, uin void DISK::set_sector_info(uint8 *t) { - // header info - id[0] = t[0]; - id[1] = t[1]; - id[2] = t[2]; - id[3] = t[3]; - uint16 crc = 0; - crc = (uint16)((crc << 8) ^ crc_table[(uint8)(crc >> 8) ^ t[0]]); - crc = (uint16)((crc << 8) ^ crc_table[(uint8)(crc >> 8) ^ t[1]]); - crc = (uint16)((crc << 8) ^ crc_table[(uint8)(crc >> 8) ^ t[2]]); - crc = (uint16)((crc << 8) ^ crc_table[(uint8)(crc >> 8) ^ t[3]]); - id[4] = (crc >> 8) & 0xff; - id[5] = (crc >> 0) & 0xff; + // header info + int am_size = track_mfm ? 3 : 0; + uint16 crc = 0xffff; + for(int i = 0; i < am_size; i++) { + crc = (uint16)((crc << 8) ^ crc_table[(uint8)(crc >> 8) ^ 0xa1]); + } + crc = (uint16)((crc << 8) ^ crc_table[(uint8)(crc >> 8) ^ 0xfe]); + id[0] = t[0]; + id[1] = t[1]; + id[2] = t[2]; + id[3] = t[3]; + crc = (uint16)((crc << 8) ^ crc_table[(uint8)(crc >> 8) ^ t[0]]); + crc = (uint16)((crc << 8) ^ crc_table[(uint8)(crc >> 8) ^ t[1]]); + crc = (uint16)((crc << 8) ^ crc_table[(uint8)(crc >> 8) ^ t[2]]); + crc = (uint16)((crc << 8) ^ crc_table[(uint8)(crc >> 8) ^ t[3]]); + id[4] = (crc >> 8) & 0xff; + id[5] = (crc >> 0) & 0xff; // http://www,gnu-darwin.or.jp/www001/src/ports/emulators/quasi88/work/quasi88-0.6.3/document/FORMAT.TXT // t[6]: 0x00 = double-density, 0x40 = single-density // t[7]: 0x00 = normal, 0x10 = deleted mark // t[8]: 0x00 = valid, 0x10 = valid (deleted data), 0xa0 = id crc error, 0xb0 = data crc error, 0xe0 = address mark missing, 0xf0 = data mark missing - density = t[6]; - deleted = (t[7] != 0); - if(config.ignore_crc) { - crc_error = false; - } else { - crc_error = ((t[8] & 0xf0) != 0x00 && (t[8] & 0xf0) != 0x10); - } - sector = t + 0x10; - sector_size.read_2bytes_le_from(t + 14); -} + density = t[6]; + sector_mfm = (density == 0x00); + deleted = (t[7] != 0); + if(config.ignore_crc) { + addr_crc_error = false; + data_crc_error = false; + } else { + addr_crc_error = ((t[8] & 0xf0) == 0xa0); + data_crc_error = ((t[8] & 0xf0) == 0xb0); + } + crc_error = addr_crc_error || data_crc_error; + sector = t + 0x10; + sector_size.read_2bytes_le_from(t + 14); +} void DISK::set_deleted(bool value) { @@ -731,10 +907,12 @@ void DISK::set_deleted(bool value) void DISK::set_crc_error(bool value) { - if(sector != NULL) { - uint8 *t = sector - 0x10; - t[8] = (t[8] & 0x0f) | (value ? 0xb0 : t[7]); // FIXME: always data crc error ? - } + if(sector != NULL) { + uint8 *t = sector - 0x10; + t[8] = (t[8] & 0x0f) | (value ? 0xb0 : t[7]); + } + addr_crc_error = false; + data_crc_error = value; crc_error = value; } @@ -745,14 +923,26 @@ void DISK::set_data_mark_missing() t[8] = (t[8] & 0x0f) | 0xf0; t[14] = t[15] = 0; } + addr_crc_error = false; + data_crc_error = false; crc_error = false; } -bool DISK::format_track(int trk, int side) -{ - // disk not inserted or invalid media type - if(!(inserted && check_media_type())) { - return false; +bool DISK::format_track(int trk, int side) +{ + if(media_type == MEDIA_TYPE_2D && drive_type == DRIVE_TYPE_2DD) { + if(trk >= 0) { + if(trk & 1) { + return false; + } + trk >>= 1; + } + } else if(media_type == MEDIA_TYPE_2DD && drive_type == DRIVE_TYPE_2D) { + if(trk >= 0) trk <<= 1; + } + // disk not inserted or invalid media type + if(!(inserted && check_media_type())) { + return false; } // search track @@ -773,6 +963,8 @@ bool DISK::format_track(int trk, int side) trim_required = true; sector_num.sd = 0; + sector = NULL; + unstable = NULL; track_mfm = drive_mfm; return true; } @@ -1645,13 +1837,17 @@ bool DISK::load_state(FILEIO* state_fio) state_fio->Fread(sync_position, sizeof(sync_position), 1); state_fio->Fread(id_position, sizeof(id_position), 1); state_fio->Fread(data_position, sizeof(data_position), 1); - int offset = state_fio->FgetInt32(); - sector = (offset != -1) ? buffer + offset : NULL; - sector_size.sd = state_fio->FgetInt32(); - state_fio->Fread(id, sizeof(id), 1); - density = state_fio->FgetUint8(); - deleted = state_fio->FgetBool(); - crc_error = state_fio->FgetBool(); + int offset = state_fio->FgetInt32(); + sector = (offset != -1) ? buffer + offset : NULL; + unstable = NULL; + sector_size.sd = state_fio->FgetInt32(); + state_fio->Fread(id, sizeof(id), 1); + density = state_fio->FgetUint8(); + sector_mfm = (density == 0x00); + deleted = state_fio->FgetBool(); + crc_error = state_fio->FgetBool(); + addr_crc_error = false; + data_crc_error = crc_error; drive_type = state_fio->FgetUint8(); drive_rpm = state_fio->FgetInt32(); drive_mfm = state_fio->FgetBool(); diff --git a/Source/ePC-8801MA/vm/disk.h b/Source/ePC-8801MA/vm/disk.h index 35c2d98..81027ed 100644 --- a/Source/ePC-8801MA/vm/disk.h +++ b/Source/ePC-8801MA/vm/disk.h @@ -48,8 +48,8 @@ class DISK { protected: EMU* emu; -private: - FILEIO* fi; +private: + FILEIO* fi; uint8 buffer[DISK_BUFFER_SIZE + TRACK_BUFFER_SIZE]; _TCHAR orig_path[_MAX_PATH]; _TCHAR dest_path[_MAX_PATH]; @@ -57,9 +57,11 @@ class DISK pair file_size; int file_bank; uint32 crc32; - bool trim_required; - bool temporary; - uint8 fdi_header[4096]; + bool trim_required; + bool temporary; + uint8 fdi_header[4096]; + int get_track_num(uint8* t); + uint8* get_unstable_sector(uint8* t, int index, uint8 c, uint8 h, uint8 r, uint8 n, int size); void set_sector_info(uint8 *t); void trim_buffer(); @@ -147,10 +149,15 @@ class DISK public: DISK(EMU* parent_emu) : emu(parent_emu) { - inserted = ejected = write_protected = changed = false; - file_size.d = 0; - sector_size.sd = sector_num.sd = 0; - sector = NULL; + inserted = ejected = write_protected = changed = false; + file_size.d = 0; + sector_size.sd = sector_num.sd = 0; + sector = NULL; + unstable = NULL; + sector_mfm = true; + addr_crc_error = false; + data_crc_error = false; + crc_error = false; drive_type = DRIVE_TYPE_UNK; drive_rpm = 0; drive_mfm = true; @@ -225,13 +232,17 @@ class DISK int id_position[256]; int data_position[256]; - // sector - uint8* sector; - pair sector_size; - uint8 id[6]; - uint8 density; - bool deleted; - bool crc_error; + // sector + uint8* sector; + uint8* unstable; + pair sector_size; + uint8 id[6]; + uint8 density; + bool sector_mfm; + bool deleted; + bool addr_crc_error; + bool data_crc_error; + bool crc_error; // drive uint8 drive_type; diff --git a/Source/ePC-8801MA/vm/event.cpp b/Source/ePC-8801MA/vm/event.cpp index d4c91ee..b4677d8 100644 --- a/Source/ePC-8801MA/vm/event.cpp +++ b/Source/ePC-8801MA/vm/event.cpp @@ -7,19 +7,19 @@ [ event manager ] */ -#include "event.h" +#include "event.h" #ifdef SDL #include "z80.h" #endif // SDL #define EVENT_MIX 0 -#ifdef SDL -#define SINGLE_EXEC_TIMEOUT 10000 - // exit single exec mode (us) -#endif // SDL - -void EVENT::initialize() +#ifdef SDL +#define SINGLE_EXEC_TIMEOUT 10000 + // exit single exec mode (us) +#endif // SDL + +void EVENT::initialize() { // load config if(!(0 <= config.cpu_power && config.cpu_power <= 4)) { @@ -127,9 +127,9 @@ void EVENT::reset() } #ifdef SDL -void EVENT::request_single_exec() -{ - if (single_exec == false) { +void EVENT::request_single_exec() +{ + if (single_exec == false) { // enter single execution mode single_exec = true; @@ -143,15 +143,15 @@ void EVENT::request_single_exec() } #endif // SDL -void EVENT::drive() -{ -#ifdef SDL - if (single_exec == true) { - // if passed 10ms, disable single_exec - if (passed_usec(single_exec_clock) > SINGLE_EXEC_TIMEOUT) { - single_exec = false; - } - } +void EVENT::drive() +{ +#ifdef SDL + if (single_exec == true) { + // if passed 10ms, disable single_exec + if (passed_usec(single_exec_clock) > SINGLE_EXEC_TIMEOUT) { + single_exec = false; + } + } #endif // SDL // raise pre frame events to update timing settings @@ -246,15 +246,15 @@ void EVENT::drive() main_cpu_exec = min_event_clock; } - if (main_cpu_exec > 0) { - // single execution ? - if (single_exec == true) { - if (main_cpu_exec > 4) { - main_cpu_exec = 4; - } - } - cpu_done = d_cpu[0].device->run(main_cpu_exec); - } + if (main_cpu_exec > 0) { + // single execution ? + if (single_exec == true) { + if (main_cpu_exec > 4) { + main_cpu_exec = 4; + } + } + cpu_done = d_cpu[0].device->run(main_cpu_exec); + } // if registered new event during main cpu execution, recalc min_event_clock if (regist_event_ctrl == true) { @@ -267,11 +267,11 @@ void EVENT::drive() // main 4MHz if (cpu_remain > 0) { sub_cpu_exec = cpu_remain; - if (single_exec == true) { - if (sub_cpu_exec > 4) { - sub_cpu_exec = 4; - } - } + if (single_exec == true) { + if (sub_cpu_exec > 4) { + sub_cpu_exec = 4; + } + } sub_cpu_done = d_cpu[1].device->run(sub_cpu_exec); cpu_remain -= sub_cpu_done; @@ -281,11 +281,11 @@ void EVENT::drive() // main 8MHz if (cpu_remain > 2) { sub_cpu_exec = cpu_remain / 2; - if (single_exec == true) { - if (sub_cpu_exec > 4) { - sub_cpu_exec = 4; - } - } + if (single_exec == true) { + if (sub_cpu_exec > 4) { + sub_cpu_exec = 4; + } + } sub_cpu_done = d_cpu[1].device->run(sub_cpu_exec); cpu_remain -= (sub_cpu_done * 2); diff --git a/Source/ePC-8801MA/vm/upd765a.cpp b/Source/ePC-8801MA/vm/upd765a.cpp index 0e08d4c..5bdb6a1 100644 --- a/Source/ePC-8801MA/vm/upd765a.cpp +++ b/Source/ePC-8801MA/vm/upd765a.cpp @@ -11,6 +11,9 @@ #include "upd765a.h" #include "disk.h" #include "noise.h" +#include +#include +#include #define EVENT_PHASE 0 #define EVENT_DRQ 1 @@ -69,7 +72,164 @@ #define ST3_WP 0x40 #define ST3_FT 0x80 -#define DRIVE_MASK 3 +#define DRIVE_MASK 3 + +static FILE* g_fdc_trace_file = NULL; +static bool g_fdc_trace_checked = false; +static int g_fdc_trace_lines = 0; +static int g_fdc_trace_max_lines = 200000; +static bool g_fdc_trace_io_checked = false; +static bool g_fdc_trace_io = false; +static bool g_fdc_seek_scale_checked = false; +static int g_fdc_seek_scale = 1000; +static bool g_fdc_disable_unstable_checked = false; +static bool g_fdc_disable_unstable = true; +static bool g_fdc_const_exec_checked = false; +static bool g_fdc_const_exec = true; +static bool g_fdc_request_single_exec_checked = false; +static bool g_fdc_request_single_exec = true; +static bool g_fdc_tc_exec_checked = false; +static bool g_fdc_tc_exec = true; +static bool g_fdc_lost_usec_checked = false; +static int g_fdc_lost_usec = 15000; + +static bool fdc_trace_enabled() +{ + if(!g_fdc_trace_checked) { + const char* env = getenv("XM8_FDC_TRACE"); + if(env != NULL && env[0] != '\0' && env[0] != '0') { + const char* trace_path = getenv("XM8_FDC_TRACE_FILE"); + if(trace_path != NULL && trace_path[0] != '\0') { + g_fdc_trace_file = fopen(trace_path, "w"); + } else { + g_fdc_trace_file = fopen("xm8_fdc_trace.log", "w"); + } + } + const char* max_lines = getenv("XM8_FDC_TRACE_MAX_LINES"); + if(max_lines != NULL && max_lines[0] != '\0') { + long v = strtol(max_lines, NULL, 10); + if(v > 0 && v <= 2000000) { + g_fdc_trace_max_lines = (int)v; + } + } + g_fdc_trace_checked = true; + } + return g_fdc_trace_file != NULL; +} + +static bool fdc_trace_io_enabled() +{ + if(!g_fdc_trace_io_checked) { + const char* env = getenv("XM8_FDC_TRACE_IO"); + g_fdc_trace_io = (env != NULL && env[0] != '\0' && env[0] != '0'); + g_fdc_trace_io_checked = true; + } + return g_fdc_trace_io; +} + +static int fdc_seek_scale() +{ + if(!g_fdc_seek_scale_checked) { + const char* env = getenv("XM8_FDC_SEEK_SCALE"); + if(env != NULL && env[0] != '\0') { + long v = strtol(env, NULL, 10); + if(v >= 1 && v <= 10000) { + g_fdc_seek_scale = (int)v; + } + } + g_fdc_seek_scale_checked = true; + } + return g_fdc_seek_scale; +} + +static bool fdc_disable_unstable_mask() +{ + if(!g_fdc_disable_unstable_checked) { + const char* env = getenv("XM8_DISABLE_UNSTABLE_MASK"); + g_fdc_disable_unstable = (env != NULL && env[0] != '\0' && env[0] != '0'); + g_fdc_disable_unstable_checked = true; + } + return g_fdc_disable_unstable; +} + +static bool fdc_const_exec_timing() +{ + if(!g_fdc_const_exec_checked) { + const char* env = getenv("XM8_FDC_CONST_EXEC_USEC"); + g_fdc_const_exec = (env != NULL && env[0] != '\0' && env[0] != '0'); + g_fdc_const_exec_checked = true; + } + return g_fdc_const_exec; +} + +static bool fdc_request_single_exec_enabled() +{ + if(!g_fdc_request_single_exec_checked) { + const char* env = getenv("XM8_FDC_REQUEST_SINGLE_EXEC"); + if(env != NULL && env[0] != '\0') { + g_fdc_request_single_exec = (env[0] != '0'); + } + g_fdc_request_single_exec_checked = true; + } + return g_fdc_request_single_exec; +} + +static bool fdc_tc_exec_enabled() +{ + if(!g_fdc_tc_exec_checked) { + const char* env = getenv("XM8_FDC_TC_EXEC"); + if(env != NULL && env[0] != '\0') { + g_fdc_tc_exec = (env[0] != '0'); + } + g_fdc_tc_exec_checked = true; + } + return g_fdc_tc_exec; +} + +static int fdc_lost_event_usec() +{ + if(!g_fdc_lost_usec_checked) { + const char* env = getenv("XM8_FDC_LOST_USEC"); + if(env != NULL && env[0] != '\0') { + long v = strtol(env, NULL, 10); + if(v >= 1 && v <= 10000000) { + g_fdc_lost_usec = (int)v; + } else if(v <= 0) { + g_fdc_lost_usec = 0; + } + } + g_fdc_lost_usec_checked = true; + } + return g_fdc_lost_usec; +} + +static void fdc_trace(const char* fmt, ...) +{ + if(!fdc_trace_enabled()) { + return; + } + if(g_fdc_trace_lines > g_fdc_trace_max_lines) { + return; + } + va_list ap; + va_start(ap, fmt); + vfprintf(g_fdc_trace_file, fmt, ap); + va_end(ap); + fputc('\n', g_fdc_trace_file); + fflush(g_fdc_trace_file); + g_fdc_trace_lines++; +} + +static uint32 fdc_hash_bytes(const uint8* data, int len) +{ + // FNV-1a 32-bit for lightweight sector content tracing. + uint32 hash = 2166136261u; + for(int i = 0; i < len; i++) { + hash ^= data[i]; + hash *= 16777619u; + } + return hash; +} #define REGISTER_PHASE_EVENT(phs, usec) { \ if(phase_id != -1) { \ @@ -155,6 +315,7 @@ void UPD765A::initialize() { + fdc_trace("=== FDC trace start ==="); // initialize d88 handler for(int i = 0; i < 4; i++) { disk[i] = new DISK(emu); @@ -190,6 +351,13 @@ void UPD765A::initialize() phase = prevphase = PHASE_IDLE; status = S_RQM; seekstat = 0; + command = 0; + result = 0; + hdue = 0; + id[0] = id[1] = id[2] = id[3] = 0; + eot = 0; + gpl = 0; + dtl = 0; bufptr = buffer; // temporary phase_id = drq_id = lost_id = result7_id = -1; seek_id[0] = seek_id[1] = seek_id[2] = seek_id[3] = -1; @@ -218,30 +386,83 @@ void UPD765A::initialize() } } -void UPD765A::release() -{ - for(int i = 0; i < 4; i++) { - if(disk[i]) { - disk[i]->close(); - delete disk[i]; - } - } -} +void UPD765A::release() +{ + for(int i = 0; i < 4; i++) { + if(disk[i]) { + disk[i]->close(); + delete disk[i]; + } + } + if(g_fdc_trace_file != NULL) { + fdc_trace("=== FDC trace end ==="); + fclose(g_fdc_trace_file); + g_fdc_trace_file = NULL; + g_fdc_trace_checked = false; + g_fdc_trace_lines = 0; + g_fdc_trace_max_lines = 200000; + g_fdc_trace_io_checked = false; + g_fdc_trace_io = false; + } + g_fdc_disable_unstable_checked = false; + g_fdc_disable_unstable = true; + g_fdc_const_exec_checked = false; + g_fdc_const_exec = true; + g_fdc_request_single_exec_checked = false; + g_fdc_request_single_exec = true; + g_fdc_tc_exec_checked = false; + g_fdc_tc_exec = true; + g_fdc_lost_usec_checked = false; + g_fdc_lost_usec = 15000; +} void UPD765A::reset() { shift_to_idle(); -// CANCEL_EVENT(); - phase_id = drq_id = lost_id = result7_id = -1; - seek_id[0] = seek_id[1] = seek_id[2] = seek_id[3] = -1; - seek_step_id[0] = seek_step_id[1] = seek_step_id[2] = seek_step_id[3] = -1; - seek_step_remain[0] = seek_step_remain[1] = seek_step_remain[2] = seek_step_remain[3] = 0; - head_unload_id[0] = head_unload_id[1] = head_unload_id[2] = head_unload_id[3] = -1; + if(phase_id != -1) { + cancel_event(this, phase_id); + phase_id = -1; + } + if(drq_id != -1) { + cancel_event(this, drq_id); + drq_id = -1; + } + if(lost_id != -1) { + cancel_event(this, lost_id); + lost_id = -1; + } + if(result7_id != -1) { + cancel_event(this, result7_id); + result7_id = -1; + } + for(int i = 0; i < 4; i++) { + if(seek_id[i] != -1) { + cancel_event(this, seek_id[i]); + } + if(seek_step_id[i] != -1) { + // Loop events are not guaranteed to be canceled by EVENT::reset(). + cancel_event(this, seek_step_id[i]); + } + if(head_unload_id[i] != -1) { + cancel_event(this, head_unload_id[i]); + } + seek_id[i] = -1; + seek_step_id[i] = -1; + seek_step_remain[i] = 0; + head_unload_id[i] = -1; + } head_load[0] = head_load[1] = head_load[2] = head_load[3] = false; cur_track[0] = fdc[0].track; cur_track[1] = fdc[1].track; cur_track[2] = fdc[2].track; cur_track[3] = fdc[3].track; + command = 0; + result = 0; + hdue = hdu; + id[0] = id[1] = id[2] = id[3] = 0; + eot = 0; + gpl = 0; + dtl = 0; set_irq(false); set_drq(false); @@ -360,17 +581,22 @@ uint32 UPD765A::read_io8(uint32 addr) uint8 data; status &= ~S_RQM; - switch(phase) { - case PHASE_RESULT: - data = *bufptr++; -#ifdef _FDC_DEBUG_LOG - emu->out_debug_log("FDC: RESULT=%2x\n", data); -#endif - if(--count) { - status |= S_RQM; - } else { - // EPSON QC-10 CP/M Plus - bool clear_irq = true; + switch(phase) { + case PHASE_RESULT: + { + int idx = (int)(bufptr - buffer); + data = *bufptr++; +#ifdef _FDC_DEBUG_LOG + emu->out_debug_log("FDC: RESULT=%2x\n", data); +#endif + if(fdc_trace_io_enabled()) { + fdc_trace("result_byte: cmd=%02x idx=%d data=%02x", command, idx, data); + } + if(--count) { + status |= S_RQM; + } else { + // EPSON QC-10 CP/M Plus + bool clear_irq = true; if((command & 0x1f) == 0x08) { for(int i = 0; i < 4; i++) { if(fdc[i].result) { @@ -382,11 +608,12 @@ uint32 UPD765A::read_io8(uint32 addr) if(clear_irq) { set_irq(false); } - shift_to_idle(); - } - return data; - - case PHASE_READ: + shift_to_idle(); + } + return data; + } + + case PHASE_READ: data = *bufptr++; #ifdef _FDC_DEBUG_LOG emu->out_debug_log("FDC: READ=%2x\n", data); @@ -444,18 +671,18 @@ void UPD765A::write_signal(int id, uint32 data, uint32 mask) reset(); } reset_signal = next; - } else if(id == SIG_UPD765A_TC) { -#ifdef SDL - // phase==PHASE_EXEC support (for Xanadu Scenario II) - if(phase == PHASE_READ || phase == PHASE_WRITE || phase == PHASE_SCAN || (phase == PHASE_RESULT && count == 7) || (phase == PHASE_EXEC)) { -#else - if(phase == PHASE_READ || phase == PHASE_WRITE || phase == PHASE_SCAN || (phase == PHASE_RESULT && count == 7)) { -#endif // SDL - if(data & mask) { - prevphase = phase; - phase = PHASE_TC; - process_cmd(command & 0x1f); - } + } else if(id == SIG_UPD765A_TC) { +#ifdef SDL + // phase==PHASE_EXEC support (for Xanadu Scenario II) + if(phase == PHASE_READ || phase == PHASE_WRITE || phase == PHASE_SCAN || (phase == PHASE_RESULT && count == 7) || (fdc_tc_exec_enabled() && phase == PHASE_EXEC)) { +#else + if(phase == PHASE_READ || phase == PHASE_WRITE || phase == PHASE_SCAN || (phase == PHASE_RESULT && count == 7)) { +#endif // SDL + if(data & mask) { + prevphase = phase; + phase = PHASE_TC; + process_cmd(command & 0x1f); + } } } else if(id == SIG_UPD765A_MOTOR) { motor_on = ((data & mask) != 0); @@ -495,13 +722,15 @@ uint32 UPD765A::read_signal(int ch) void UPD765A::event_callback(int event_id, int err) { -#ifdef SDL - request_single_exec(); -#endif // SDL - if(event_id == EVENT_PHASE) { - phase_id = -1; - phase = event_phase; - process_cmd(command & 0x1f); +#ifdef SDL + if(fdc_request_single_exec_enabled()) { + request_single_exec(); + } +#endif // SDL + if(event_id == EVENT_PHASE) { + phase_id = -1; + phase = event_phase; + process_cmd(command & 0x1f); } else if(event_id == EVENT_DRQ) { drq_id = -1; status |= S_RQM; @@ -596,21 +825,26 @@ void UPD765A::set_drq(bool val) } drq_id = lost_id = -1; // register data lost event if data exists - if(val) { -#ifdef UPD765A_DMA_MODE + if(val) { +#ifdef UPD765A_DMA_MODE // EPSON QC-10 CP/M Plus dma_data_lost = true; -#else - if((command & 0x1f) != 0x0d) { -#ifdef SDL - // for customized event manager + 2HD disk - register_event(this, EVENT_LOST, 30000, false, &lost_id); -#else - register_event(this, EVENT_LOST, disk[hdu & DRIVE_MASK]->get_usec_per_bytes(1), false, &lost_id); -#endif // SDL - } else { - // FIXME: write id - register_event(this, EVENT_LOST, 30000, false, &lost_id); +#else + if((command & 0x1f) != 0x0d) { +#ifdef SDL + // for customized event manager + 2HD disk + int usec = fdc_lost_event_usec(); + if(usec > 0) { + register_event(this, EVENT_LOST, usec, false, &lost_id); + } else { + register_event(this, EVENT_LOST, disk[hdu & DRIVE_MASK]->get_usec_per_bytes(1), false, &lost_id); + } +#else + register_event(this, EVENT_LOST, disk[hdu & DRIVE_MASK]->get_usec_per_bytes(1), false, &lost_id); +#endif // SDL + } else { + // FIXME: write id + register_event(this, EVENT_LOST, 30000, false, &lost_id); } #endif } @@ -646,9 +880,10 @@ void UPD765A::set_hdu(uint8 val) // command // ---------------------------------------------------------------------------- -void UPD765A::process_cmd(int cmd) -{ - switch(cmd & 0x1f) { +void UPD765A::process_cmd(int cmd) +{ + fdc_trace("cmd=%02x op=%02x phase=%d hdu=%d C=%02x H=%02x R=%02x N=%02x", command, cmd & 0x1f, phase, hdu & DRIVE_MASK, id[0], id[1], id[2], id[3]); + switch(cmd & 0x1f) { case 0x02: cmd_read_diagnostic(); break; @@ -698,44 +933,51 @@ void UPD765A::cmd_sence_devstat() case PHASE_IDLE: shift_to_cmd(1); break; - case PHASE_CMD: - set_hdu(buffer[0]); - buffer[0] = get_devstat(buffer[0] & DRIVE_MASK); - shift_to_result(1); - break; - } -} + case PHASE_CMD: + set_hdu(buffer[0]); + buffer[0] = get_devstat(buffer[0] & DRIVE_MASK); + fdc_trace("sense_devstat: drv=%d st3=%02x cur=%d tgt=%d", buffer[0] & DRIVE_MASK, buffer[0], cur_track[buffer[0] & DRIVE_MASK], fdc[buffer[0] & DRIVE_MASK].track); + shift_to_result(1); + break; + } +} void UPD765A::cmd_sence_intstat() { - for(int i = 0; i < 4; i++) { - if(fdc[i].result) { - buffer[0] = (uint8)fdc[i].result; - buffer[1] = (uint8)fdc[i].track; - fdc[i].result = 0; - shift_to_result(2); - return; - } - } + for(int i = 0; i < 4; i++) { + if(fdc[i].result) { + buffer[0] = (uint8)fdc[i].result; + buffer[1] = (uint8)fdc[i].track; + fdc[i].result = 0; + fdc_trace("sense_intstat: drv=%d st0=%02x pcn=%02x", i, buffer[0], buffer[1]); + shift_to_result(2); + return; + } + } #ifdef UPD765A_SENCE_INTSTAT_RESULT // IBM PC/JX buffer[0] = (uint8)ST0_AI; -#else - buffer[0] = (uint8)ST0_IC; -#endif - shift_to_result(1); -// status &= ~S_CB; -} +#else + buffer[0] = (uint8)ST0_IC; +#endif + fdc_trace("sense_intstat: no_pending st0=%02x", buffer[0]); + shift_to_result(1); +// status &= ~S_CB; +} uint8 UPD765A::get_devstat(int drv) { if(drv >= MAX_DRIVE) { return ST3_FT | drv; } + // Keep ST3 track-side bit based on commanded track like common_source_project. + // Using cur_track here can introduce host-timing dependent behavior while seek + // step events are in flight. + int track_for_st3 = fdc[drv].track; return drv | - ((cur_track[drv] & 1) ? ST3_HD : 0) | + ((track_for_st3 & 1) ? ST3_HD : 0) | ST3_TS | - (cur_track[drv] ? 0 : ST3_T0) | + (track_for_st3 ? 0 : ST3_T0) | ((force_ready || disk[drv]->inserted) ? ST3_RY : 0) | (disk[drv]->write_protected ? ST3_WP : 0); } @@ -768,8 +1010,6 @@ void UPD765A::cmd_recalib() void UPD765A::seek(int drv, int trk) { - // get distance - int distance = abs(trk - cur_track[drv]); int steptime = 32 - 2 * step_rate_time; if(disk[drv]->drive_type == DRIVE_TYPE_2HD) { steptime /= 2; @@ -777,13 +1017,24 @@ void UPD765A::seek(int drv, int trk) if(steptime <= 0) { steptime = 1; } - int seektime = (distance == 0) ? 120 : steptime * distance + 500; // usec + int seek_scale = fdc_seek_scale(); + int steptime_usec = steptime * seek_scale; + if(steptime_usec <= 0) { + steptime_usec = 1; + } if(drv >= MAX_DRIVE) { // invalid drive number fdc[drv].result = (drv & DRIVE_MASK) | ST0_SE | ST0_NR | ST0_AT; set_irq(true); } else { + // Match common_source_project: SEEK timing is based on previous + // commanded track, not current stepped track. + int prev_track = fdc[drv].track; + int distance = abs(trk - prev_track); + int seektime = (distance == 0) ? 120 : steptime_usec * distance + 500; // usec + fdc_trace("seek: drv=%d from=%d to=%d dist=%d steptime=%d steptime_us=%d scale=%d seektime=%d", drv, prev_track, trk, distance, steptime, steptime_usec, seek_scale, seektime); + cur_track[drv] = prev_track; fdc[drv].track = trk; #ifdef UPD765A_DONT_WAIT_SEEK if(distance > 0 && d_noise_seek != NULL) { @@ -803,12 +1054,11 @@ void UPD765A::seek(int drv, int trk) #ifdef SDL if (config.ignore_crc) { seektime = 100; - distance = 0; } #endif // SDL if(distance > 0) { seek_step_remain[drv] = distance; - register_event(this, EVENT_SEEK_STEP + drv, steptime, true, &seek_step_id[drv]); + register_event(this, EVENT_SEEK_STEP + drv, steptime_usec, true, &seek_step_id[drv]); } else { cur_track[drv] = fdc[drv].track; } @@ -832,8 +1082,9 @@ void UPD765A::seek_event(int drv) fdc[drv].result = (drv & DRIVE_MASK) | ST0_SE | ST0_NR | ST0_AT; #endif } - set_irq(true); - seekstat &= ~(1 << drv); + set_irq(true); + seekstat &= ~(1 << drv); + fdc_trace("seek_end: drv=%d cur=%d target=%d result=%02x", drv, cur_track[drv], fdc[drv].track, fdc[drv].result); // reset dsch flag disk[drv]->changed = false; @@ -1090,20 +1341,39 @@ void UPD765A::read_diagnostic() REGISTER_PHASE_EVENT(PHASE_EXEC, 10000); return; } - if(result) { - shift_to_result7(); - return; - } - if(!disk[drv]->make_track(trk, side)) { - result = ST1_ND; - shift_to_result7(); - return; - } - - // read from the 1st sector data - memcpy(buffer, disk[drv]->track + disk[drv]->data_position[0], disk[drv]->get_track_size() - disk[drv]->data_position[0]); - memcpy(buffer + disk[drv]->get_track_size() - disk[drv]->data_position[0], disk[drv]->track, disk[drv]->data_position[0]); - fdc[drv].next_trans_position = disk[drv]->data_position[0]; + if(result) { + shift_to_result7(); + return; + } + if(!disk[drv]->make_track(trk, side)) { + result = ST0_AT | ST1_MA; + shift_to_result7(); + return; + } + bool found = false; + for(int i = 0; i < disk[drv]->sector_num.sd; i++) { + if(!disk[drv]->get_sector(trk, side, i)) { + continue; + } + if((command & 0x40) != (disk[drv]->sector_mfm ? 0x40 : 0)) { + continue; + } + found = true; + if(disk[drv]->id[0] != id[0] || disk[drv]->id[1] != id[1] || disk[drv]->id[2] != id[2] || disk[drv]->id[3] != id[3]) { + result = ST1_ND; + } + break; + } + if(!found) { + result = ST0_AT | ST1_MA; + shift_to_result7(); + return; + } + + // read from the 1st sector data + memcpy(buffer, disk[drv]->track + disk[drv]->data_position[0], disk[drv]->get_track_size() - disk[drv]->data_position[0]); + memcpy(buffer + disk[drv]->get_track_size() - disk[drv]->data_position[0], disk[drv]->track, disk[drv]->data_position[0]); + fdc[drv].next_trans_position = disk[drv]->data_position[0]; shift_to_read(0x80 << min((int)id[3], 7)); return; @@ -1116,25 +1386,27 @@ uint32 UPD765A::read_sector() int side = (hdu >> 2) & 1; // get sector counts in the current track - if(!disk[drv]->make_track(trk, side)) { -#ifdef _FDC_DEBUG_LOG - emu->out_debug_log("FDC: TRACK NOT FOUND (TRK=%d SIDE=%d)\n", trk, side); -#endif - return ST0_AT | ST1_MA; - } + if(!disk[drv]->make_track(trk, side)) { +#ifdef _FDC_DEBUG_LOG + emu->out_debug_log("FDC: TRACK NOT FOUND (TRK=%d SIDE=%d)\n", trk, side); +#endif + fdc_trace("read_sector: track not found drv=%d trk=%d side=%d", drv, trk, side); + return ST0_AT | ST1_MA; + } int secnum = disk[drv]->sector_num.sd; - if(!secnum) { -#ifdef _FDC_DEBUG_LOG - emu->out_debug_log("FDC: NO SECTORS IN TRACK (TRK=%d SIDE=%d)\n", trk, side); -#endif - return ST0_AT | ST1_MA; - } + if(!secnum) { +#ifdef _FDC_DEBUG_LOG + emu->out_debug_log("FDC: NO SECTORS IN TRACK (TRK=%d SIDE=%d)\n", trk, side); +#endif + fdc_trace("read_sector: no sectors drv=%d trk=%d side=%d", drv, trk, side); + return ST0_AT | ST1_MA; + } int cy = -1; for(int i = 0; i < secnum; i++) { if(!disk[drv]->get_sector(trk, side, i)) { continue; } - if((command & 0x40) != (disk[drv]->density == 0x00 ? 0x40 : 0)) { + if((command & 0x40) != (disk[drv]->sector_mfm ? 0x40 : 0)) { continue; } cy = disk[drv]->id[0]; @@ -1146,41 +1418,64 @@ uint32 UPD765A::read_sector() continue; } if(disk[drv]->sector_size.sd == 0) { + fdc_trace("read_sector: zero size drv=%d trk=%d side=%d idx=%d", drv, trk, side, i); continue; } + if(disk[drv]->invalid_format) { + fdc_trace("read_sector: invalid_format drv=%d trk=%d side=%d idx=%d unstable=%d", drv, trk, side, i, (disk[drv]->unstable != NULL) ? 1 : 0); + } // sector number is matched - if(disk[drv]->invalid_format) { - memset(buffer, disk[drv]->drive_mfm ? 0x4e : 0xff, sizeof(buffer)); - memcpy(buffer, disk[drv]->sector, disk[drv]->sector_size.sd); - } else { - memcpy(buffer, disk[drv]->track + disk[drv]->data_position[i], disk[drv]->get_track_size() - disk[drv]->data_position[i]); - memcpy(buffer + disk[drv]->get_track_size() - disk[drv]->data_position[i], disk[drv]->track, disk[drv]->data_position[i]); + if(disk[drv]->invalid_format) { + for(int j = 0; j < disk[drv]->sector_size.sd; j++) { + uint8 mask = (disk[drv]->unstable != NULL && !fdc_disable_unstable_mask()) ? disk[drv]->unstable[j] : 0; + buffer[j] = (disk[drv]->sector[j] & ~mask) | (rand() & mask); + } + memset(buffer + disk[drv]->sector_size.sd, disk[drv]->drive_mfm ? 0x4e : 0xff, sizeof(buffer) - disk[drv]->sector_size.sd); + } else { + memcpy(buffer, disk[drv]->track + disk[drv]->data_position[i], disk[drv]->get_track_size() - disk[drv]->data_position[i]); + memcpy(buffer + disk[drv]->get_track_size() - disk[drv]->data_position[i], disk[drv]->track, disk[drv]->data_position[i]); } fdc[drv].next_trans_position = disk[drv]->data_position[i]; - if(disk[drv]->crc_error && !disk[drv]->ignore_crc()) { - return ST0_AT | ST1_DE | ST2_DD; + if((disk[drv]->addr_crc_error || disk[drv]->data_crc_error) && !disk[drv]->ignore_crc()) { + fdc_trace("read_sector: crc err drv=%d trk=%d side=%d idx=%d C=%02x H=%02x R=%02x N=%02x addr=%d data=%d", drv, trk, side, i, disk[drv]->id[0], disk[drv]->id[1], disk[drv]->id[2], disk[drv]->id[3], disk[drv]->addr_crc_error ? 1 : 0, disk[drv]->data_crc_error ? 1 : 0); + return ST0_AT | ST1_DE | (disk[drv]->data_crc_error ? ST2_DD : 0); } if(disk[drv]->deleted) { + fdc_trace("read_sector: deleted mark drv=%d trk=%d side=%d idx=%d C=%02x H=%02x R=%02x N=%02x", drv, trk, side, i, disk[drv]->id[0], disk[drv]->id[1], disk[drv]->id[2], disk[drv]->id[3]); return ST2_CM; } - return 0; - } + int transfer_len = (id[3] != 0) ? (0x80 << min((int)id[3], 7)) : min((int)dtl, 0x80); + transfer_len = min(transfer_len, disk[drv]->sector_size.sd); + uint32 data_hash = fdc_hash_bytes(buffer, transfer_len); + uint8 b0 = (transfer_len > 0) ? buffer[0] : 0; + uint8 b1 = (transfer_len > 1) ? buffer[1] : 0; + uint8 b2 = (transfer_len > 2) ? buffer[2] : 0; + uint8 b3 = (transfer_len > 3) ? buffer[3] : 0; + fdc_trace("read_sector: ok drv=%d trk=%d side=%d idx=%d C=%02x H=%02x R=%02x N=%02x seclen=%d xfer=%d hash=%08x b0=%02x b1=%02x b2=%02x b3=%02x", + drv, trk, side, i, disk[drv]->id[0], disk[drv]->id[1], disk[drv]->id[2], disk[drv]->id[3], + disk[drv]->sector_size.sd, transfer_len, data_hash, b0, b1, b2, b3); + return 0; + } #ifdef _FDC_DEBUG_LOG emu->out_debug_log("FDC: SECTOR NOT FOUND (TRK=%d SIDE=%d ID=%2x,%2x,%2x,%2x)\n", trk, side, id[0], id[1], id[2], id[3]); #endif if(cy == -1) { + fdc_trace("read_sector: no density/id match drv=%d trk=%d side=%d req=%02x,%02x,%02x,%02x", drv, trk, side, id[0], id[1], id[2], id[3]); return ST0_AT | ST1_MA; } if(cy != id[0] && cy != -1) { if(cy == 0xff) { + fdc_trace("read_sector: bad cylinder drv=%d trk=%d side=%d reqC=%02x gotC=%02x", drv, trk, side, id[0], cy); return ST0_AT | ST1_ND | ST2_BC; - } else { - return ST0_AT | ST1_ND | ST2_NC; - } - } - return ST0_AT | ST1_ND; -} + } else { + fdc_trace("read_sector: no cylinder drv=%d trk=%d side=%d reqC=%02x gotC=%02x", drv, trk, side, id[0], cy); + return ST0_AT | ST1_ND | ST2_NC; + } + } + fdc_trace("read_sector: not found drv=%d trk=%d side=%d req=%02x,%02x,%02x,%02x", drv, trk, side, id[0], id[1], id[2], id[3]); + return ST0_AT | ST1_ND; +} uint32 UPD765A::write_sector(bool deleted) { @@ -1191,20 +1486,22 @@ uint32 UPD765A::write_sector(bool deleted) if(disk[drv]->write_protected) { return ST0_AT | ST1_NW; } - // get sector counts in the current track - if(!disk[drv]->get_track(trk, side)) { - return ST0_AT | ST1_MA; - } - int secnum = disk[drv]->sector_num.sd; - if(!secnum) { - return ST0_AT | ST1_MA; - } + // get sector counts in the current track + if(!disk[drv]->get_track(trk, side)) { + fdc_trace("find_id: track not found drv=%d trk=%d side=%d", drv, trk, side); + return ST0_AT | ST1_MA; + } + int secnum = disk[drv]->sector_num.sd; + if(!secnum) { + fdc_trace("find_id: no sectors drv=%d trk=%d side=%d", drv, trk, side); + return ST0_AT | ST1_MA; + } int cy = -1; for(int i = 0; i < secnum; i++) { if(!disk[drv]->get_sector(trk, side, i)) { continue; } - if((command & 0x40) != (disk[drv]->density == 0x00 ? 0x40 : 0)) { + if((command & 0x40) != (disk[drv]->sector_mfm ? 0x40 : 0)) { continue; } cy = disk[drv]->id[0]; @@ -1249,7 +1546,7 @@ uint32 UPD765A::find_id() if(!disk[drv]->get_sector(trk, side, i)) { continue; } - if((command & 0x40) != (disk[drv]->density == 0x00 ? 0x40 : 0)) { + if((command & 0x40) != (disk[drv]->sector_mfm ? 0x40 : 0)) { continue; } cy = disk[drv]->id[0]; @@ -1259,22 +1556,27 @@ uint32 UPD765A::find_id() if(disk[drv]->sector_size.sd == 0) { continue; } - // sector number is matched - fdc[drv].next_trans_position = disk[drv]->data_position[i]; - return 0; - } + // sector number is matched + fdc[drv].next_trans_position = disk[drv]->data_position[i]; + fdc_trace("find_id: ok drv=%d trk=%d side=%d idx=%d C=%02x H=%02x R=%02x N=%02x", drv, trk, side, i, disk[drv]->id[0], disk[drv]->id[1], disk[drv]->id[2], disk[drv]->id[3]); + return 0; + } if(cy == -1) { + fdc_trace("find_id: no density/id match drv=%d trk=%d side=%d req=%02x,%02x,%02x,%02x", drv, trk, side, id[0], id[1], id[2], id[3]); return ST0_AT | ST1_MA; } if(cy != id[0] && cy != -1) { if(cy == 0xff) { + fdc_trace("find_id: bad cylinder drv=%d trk=%d side=%d reqC=%02x gotC=%02x", drv, trk, side, id[0], cy); return ST0_AT | ST1_ND | ST2_BC; - } else { - return ST0_AT | ST1_ND | ST2_NC; - } - } - return ST0_AT | ST1_ND; -} + } else { + fdc_trace("find_id: no cylinder drv=%d trk=%d side=%d reqC=%02x gotC=%02x", drv, trk, side, id[0], cy); + return ST0_AT | ST1_ND | ST2_NC; + } + } + fdc_trace("find_id: not found drv=%d trk=%d side=%d req=%02x,%02x,%02x,%02x", drv, trk, side, id[0], id[1], id[2], id[3]); + return ST0_AT | ST1_ND; +} uint32 UPD765A::check_cond(bool write) { @@ -1431,7 +1733,7 @@ uint32 UPD765A::read_id() if(!disk[drv]->get_sector(trk, side, index)) { continue; } - if((command & 0x40) != (disk[drv]->density == 0x00 ? 0x40 : 0)) { + if((command & 0x40) != (disk[drv]->sector_mfm ? 0x40 : 0)) { continue; } id[0] = disk[drv]->id[0]; @@ -1439,8 +1741,10 @@ uint32 UPD765A::read_id() id[2] = disk[drv]->id[2]; id[3] = disk[drv]->id[3]; fdc[drv].next_trans_position = disk[drv]->id_position[index] + 6; + fdc_trace("read_id: ok drv=%d trk=%d side=%d idx=%d C=%02x H=%02x R=%02x N=%02x", drv, trk, side, index, id[0], id[1], id[2], id[3]); return 0; } + fdc_trace("read_id: not found drv=%d trk=%d side=%d req=%02x,%02x,%02x,%02x", drv, trk, side, id[0], id[1], id[2], id[3]); return ST0_AT | ST1_MA; } @@ -1458,8 +1762,8 @@ uint32 UPD765A::write_id() return ST0_AT | ST1_NW; } - disk[drv]->track_mfm = ((command & 0x40) != 0); disk[drv]->format_track(trk, side); + disk[drv]->track_mfm = ((command & 0x40) != 0); for(int i = 0; i < eot && i < 256; i++) { for(int j = 0; j < 4; j++) { id[j] = buffer[4 * i + j]; @@ -1479,6 +1783,7 @@ void UPD765A::cmd_specify() step_rate_time = buffer[0] >> 4; head_unload_time = buffer[1] >> 1; no_dma_mode = ((buffer[1] & 1) != 0); + fdc_trace("specify: srt=%d hut=%d ndma=%d", step_rate_time, head_unload_time, no_dma_mode ? 1 : 0); shift_to_idle(); status = 0x80;//0xff; break; @@ -1551,13 +1856,16 @@ void UPD765A::shift_to_scan(int length) set_drq(true); } -void UPD765A::shift_to_result(int length) -{ - phase = PHASE_RESULT; - status = S_RQM | S_CB | S_DIO; - bufptr = buffer; - count = length; -} +void UPD765A::shift_to_result(int length) +{ + phase = PHASE_RESULT; + status = S_RQM | S_CB | S_DIO; + bufptr = buffer; + count = length; + if(fdc_trace_io_enabled()) { + fdc_trace("shift_result: cmd=%02x len=%d status=%02x", command, length, status); + } +} void UPD765A::shift_to_result7() { @@ -1576,17 +1884,18 @@ void UPD765A::shift_to_result7() void UPD765A::shift_to_result7_event() { -#ifdef UPD765A_NO_ST1_EN_OR_FOR_RESULT7 - // for NEC PC-9801 (XANADU) - result &= ~(ST1_EN | ST1_OR); -#endif - buffer[0] = (result & 0xf8) | (hdue & 7); - buffer[1] = uint8(result >> 8); - buffer[2] = uint8(result >> 16); - buffer[3] = id[0]; - buffer[4] = id[1]; - buffer[5] = id[2]; - buffer[6] = id[3]; +#ifdef UPD765A_NO_ST1_EN_OR_FOR_RESULT7 + // for NEC PC-9801 (XANADU) + result &= ~(ST1_EN | ST1_OR); +#endif + buffer[0] = (result & 0xf8) | (hdue & 7); + buffer[1] = uint8(result >> 8); + buffer[2] = uint8(result >> 16); + buffer[3] = id[0]; + buffer[4] = id[1]; + buffer[5] = id[2]; + buffer[6] = id[3]; + fdc_trace("result7: cmd=%02x st0=%02x st1=%02x st2=%02x C=%02x H=%02x R=%02x N=%02x", command, buffer[0], buffer[1], buffer[2], buffer[3], buffer[4], buffer[5], buffer[6]); set_irq(true); shift_to_result(7); } @@ -1632,11 +1941,17 @@ int UPD765A::get_cur_position(int drv) return (fdc[drv].cur_position + disk[drv]->get_bytes_per_usec(passed_usec(fdc[drv].prev_clock))) % disk[drv]->get_track_size(); } -double UPD765A::get_usec_to_exec_phase() -{ - int drv = hdu & DRIVE_MASK; +double UPD765A::get_usec_to_exec_phase() +{ + int drv = hdu & DRIVE_MASK; int trk = fdc[drv].track; int side = (hdu >> 2) & 1; + + // Optional timing stabilization for troublesome titles: + // keep EXEC transition delay deterministic and independent of rotational position. + if(fdc_const_exec_timing()) { + return 100; + } // XXX: this image may have incorrect skew, so use constant period. if(!disk[drv]->correct_timing()) {