floppy: Change the internal format to flux changes, update the mfi format accordingly (keep read compatibility with the old mfi)

This commit is contained in:
Olivier Galibert 2022-03-30 14:28:37 +02:00
parent 40033c809a
commit 08f28cee11
11 changed files with 344 additions and 407 deletions

View File

@ -1127,11 +1127,6 @@ uint32_t floppy_image_device::find_position(attotime &base, const attotime &when
return res;
}
bool floppy_image_device::test_track_last_entry_warps(const std::vector<uint32_t> &buf) const
{
return !((buf[buf.size() - 1]^buf[0]) & floppy_image::MG_MASK);
}
attotime floppy_image_device::position_to_time(const attotime &base, int position) const
{
return base + attotime::from_double(position/angular_speed);
@ -1141,19 +1136,13 @@ void floppy_image_device::cache_fill_index(const std::vector<uint32_t> &buf, int
{
int cells = buf.size();
if(index != 0 || !test_track_last_entry_warps(buf)) {
cache_index = index;
cache_start_time = position_to_time(base, buf[index] & floppy_image::TIME_MASK);
} else {
cache_index = cells - 1;
cache_start_time = position_to_time(base - rev_time, buf[cache_index] & floppy_image::TIME_MASK);
}
cache_index = index;
cache_start_time = position_to_time(base, buf[index] & floppy_image::TIME_MASK);
cache_entry = buf[cache_index];
index ++;
if(index >= cells) {
index = test_track_last_entry_warps(buf) ? 1 : 0;
index = 0;
base += rev_time;
}
@ -1268,161 +1257,146 @@ void floppy_image_device::write_flux(const attotime &start, const attotime &end,
track_dirty = true;
cache_clear();
attotime base;
int start_pos = find_position(base, start);
int end_pos = find_position(base, end);
std::vector<wspan> wspans(1);
attotime base;
wspans[0].start = find_position(base, start);
wspans[0].end = find_position(base, end);
std::vector<int> trans_pos(transition_count);
for(int i=0; i != transition_count; i++)
trans_pos[i] = find_position(base, transitions[i]);
wspans[0].flux_change_positions.push_back(find_position(base, transitions[i]));
wspan_split_on_wrap(wspans);
std::vector<uint32_t> &buf = image->get_buffer(cyl, ss, subcyl);
int index;
if(!buf.empty())
index = find_index(start_pos, buf);
else {
index = 0;
if(buf.empty()) {
buf.push_back(floppy_image::MG_N);
buf.push_back(floppy_image::MG_E | 199999999);
}
uint32_t cur_mg;
if((buf[index] & floppy_image::TIME_MASK) == start_pos) {
if(index)
cur_mg = buf[index-1];
else
cur_mg = buf[buf.size() - 1];
} else
cur_mg = buf[index];
wspan_remove_damaged(wspans, buf);
wspan_write(wspans, buf);
cur_mg &= floppy_image::MG_MASK;
if(cur_mg == floppy_image::MG_N || cur_mg == floppy_image::MG_D)
cur_mg = floppy_image::MG_A;
uint32_t pos = start_pos;
int ti = 0;
int cells = buf.size();
if(transition_count != 0 && trans_pos[0] == pos) {
cur_mg = cur_mg == floppy_image::MG_A ? floppy_image::MG_B : floppy_image::MG_A;
ti ++;
}
while(pos != end_pos) {
if(buf.size() < cells+10)
buf.resize(cells+200);
uint32_t next_pos;
if(ti != transition_count)
next_pos = trans_pos[ti++];
else
next_pos = end_pos;
if(next_pos > pos)
write_zone(&buf[0], cells, index, pos, next_pos, cur_mg);
else {
write_zone(&buf[0], cells, index, pos, 200000000, cur_mg);
index = 0;
write_zone(&buf[0], cells, index, 0, next_pos, cur_mg);
}
pos = next_pos;
cur_mg = cur_mg == floppy_image::MG_A ? floppy_image::MG_B : floppy_image::MG_A;
}
buf.resize(cells);
cache_clear();
}
void floppy_image_device::write_zone(uint32_t *buf, int &cells, int &index, uint32_t spos, uint32_t epos, uint32_t mg)
void floppy_image_device::wspan_split_on_wrap(std::vector<wspan> &wspans)
{
cache_clear();
while(spos < epos) {
while(index != cells-1 && (buf[index+1] & floppy_image::TIME_MASK) <= spos)
index++;
int ne = wspans.size();
for(int i=0; i != ne; i++)
if(wspans[i].end < wspans[i].start) {
wspans.resize(wspans.size()+1);
auto &ws = wspans[i];
auto &we = wspans.back();
we.start = 0;
we.end = ws.end;
ws.end = 200000000;
int start = ws.start;
int split_index;
for(split_index = 0; split_index != ws.flux_change_positions.size(); split_index++)
if(ws.flux_change_positions[split_index] < start)
break;
if(split_index == 0)
std::swap(ws.flux_change_positions, we.flux_change_positions);
uint32_t ref_start = buf[index] & floppy_image::TIME_MASK;
uint32_t ref_end = index == cells-1 ? 200000000 : buf[index+1] & floppy_image::TIME_MASK;
uint32_t ref_mg = buf[index] & floppy_image::MG_MASK;
else {
we.flux_change_positions.resize(ws.flux_change_positions.size() - split_index);
std::copy(ws.flux_change_positions.begin() + split_index, ws.flux_change_positions.end(), we.flux_change_positions.begin());
ws.flux_change_positions.erase(ws.flux_change_positions.begin() + split_index, ws.flux_change_positions.end());
}
}
}
// Can't overwrite a damaged zone
if(ref_mg == floppy_image::MG_D) {
spos = ref_end;
continue;
void floppy_image_device::wspan_remove_damaged(std::vector<wspan> &wspans, const std::vector<uint32_t> &track)
{
for(size_t pos = 0; pos != track.size(); pos++)
if((track[pos] & floppy_image::MG_MASK) == floppy_image::MG_D) {
int start = track[pos] & floppy_image::TIME_MASK;
int end = track[pos+1] & floppy_image::TIME_MASK;
int ne = wspans.size();
for(int i=0; i != ne; i++) {
// D range outside of span range
if(wspans[i].start > end || wspans[i].end <= start)
continue;
// D range covers span range
if(wspans[i].start >= start && wspans[i].end-1 <= end) {
wspans.erase(wspans.begin() + i);
i --;
ne --;
continue;
}
// D range covers the start of the span range
if(wspans[i].start >= start && wspans[i].end-1 > end) {
wspans[i].start = end+1;
while(!wspans[i].flux_change_positions.empty() && wspans[i].flux_change_positions[0] <= end)
wspans[i].flux_change_positions.erase(wspans[i].flux_change_positions.begin());
continue;
}
// D range covers the end of the span range
if(wspans[i].start < start && wspans[i].end-1 <= end) {
wspans[i].end = start;
while(!wspans[i].flux_change_positions.empty() && wspans[i].flux_change_positions[wspans[i].flux_change_positions.size()-1] >= start)
wspans[i].flux_change_positions.erase(wspans[i].flux_change_positions.end()-1);
continue;
}
// D range is inside the span range, need to split
int id = wspans.size();
wspans.resize(id+1);
wspans[id].start = end+1;
wspans[id].end = wspans[i].end;
wspans[id].flux_change_positions = wspans[i].flux_change_positions;
wspans[i].end = start;
while(!wspans[i].flux_change_positions.empty() && wspans[i].flux_change_positions[wspans[i].flux_change_positions.size()-1] >= start)
wspans[i].flux_change_positions.erase(wspans[i].flux_change_positions.end()-1);
while(!wspans[id].flux_change_positions.empty() && wspans[id].flux_change_positions[0] <= end)
wspans[id].flux_change_positions.erase(wspans[id].flux_change_positions.begin());
}
}
}
void floppy_image_device::wspan_write(const std::vector<wspan> &wspans, std::vector<uint32_t> &track)
{
for(const auto &ws : wspans) {
unsigned si, ei;
for(si = 0; si != track.size(); si++)
if((track[si] & floppy_image::TIME_MASK) >= ws.start)
break;
for(ei = si; ei != track.size(); ei++)
if((track[ei] & floppy_image::TIME_MASK) >= ws.end)
break;
// Reduce neutral zone at the start, if there's one
if(si != track.size() && (track[si] & floppy_image::MG_MASK) == floppy_image::MG_E) {
// Neutral zone is over the whole range, split it and adapt si/ei
if(si == ei) {
track.insert(track.begin() + si, floppy_image::MG_E | (ws.start-1));
track.insert(track.begin() + si + 1, (track[si-1] & floppy_image::MG_MASK) | ws.end);
si = ei = si+1;
} else {
// Reduce the zone size
track[si] = floppy_image::MG_E | (ws.start-1);
si ++;
}
}
// Check for a neutral zone at the end and reduce it if needed
if(ei != track.size() && (track[ei] & floppy_image::MG_MASK) == floppy_image::MG_E) {
track[ei-1] = floppy_image::MG_N | ws.end;
ei --;
}
// If the zone is of the type we want, we don't need to touch it
if(ref_mg == mg) {
spos = ref_end;
continue;
}
// Clear the covered zone
track.erase(track.begin() + si, track.begin() + ei);
// Check the overlaps, act accordingly
if(spos == ref_start) {
if(epos >= ref_end) {
// Full overlap, that cell is dead, we need to see which ones we can extend
uint32_t prev_mg = index != 0 ? buf[index-1] & floppy_image::MG_MASK : ~0;
uint32_t next_mg = index != cells-1 ? buf[index+1] & floppy_image::MG_MASK : ~0;
if(prev_mg == mg) {
if(next_mg == mg) {
// Both match, merge all three in one
memmove(buf+index, buf+index+2, (cells-index-2)*sizeof(uint32_t));
cells -= 2;
index--;
} else {
// Previous matches, drop the current cell
memmove(buf+index, buf+index+1, (cells-index-1)*sizeof(uint32_t));
cells --;
}
} else {
if(next_mg == mg) {
// Following matches, extend it
memmove(buf+index, buf+index+1, (cells-index-1)*sizeof(uint32_t));
cells --;
buf[index] = mg | spos;
} else {
// None match, convert the current cell
buf[index] = mg | spos;
index++;
}
}
spos = ref_end;
} else {
// Overlap at the start only
// Check if we can just extend the previous cell
if(index != 0 && (buf[index-1] & floppy_image::MG_MASK) == mg)
buf[index] = ref_mg | epos;
else {
// Otherwise we need to insert a new cell
if(index != cells-1)
memmove(buf+index+1, buf+index, (cells-index)*sizeof(uint32_t));
cells++;
buf[index] = mg | spos;
buf[index+1] = ref_mg | epos;
}
spos = epos;
}
} else {
if(epos >= ref_end) {
// Overlap at the end only
// If we can't just extend the following cell, we need to insert a new one
if(index == cells-1 || (buf[index+1] & floppy_image::MG_MASK) != mg) {
if(index != cells-1)
memmove(buf+index+2, buf+index+1, (cells-index-1)*sizeof(uint32_t));
cells++;
}
buf[index+1] = mg | spos;
index++;
spos = ref_end;
} else {
// Full inclusion
// We need to split the zone in 3
if(index != cells-1)
memmove(buf+index+3, buf+index+1, (cells-index-1)*sizeof(uint32_t));
cells += 2;
buf[index+1] = mg | spos;
buf[index+2] = ref_mg | epos;
spos = epos;
}
// Insert the flux changes
for(auto f : ws.flux_change_positions) {
track.insert(track.begin() + si, floppy_image::MG_F | f);
si ++;
}
}
}

View File

@ -228,7 +228,6 @@ protected:
attotime revolution_start_time, rev_time;
uint32_t revolution_count;
int cyl, subcyl;
/* Current floppy zone cache */
attotime cache_start_time, cache_end_time, cache_weak_start;
attotime amplifier_freakout_time;
@ -246,15 +245,24 @@ protected:
wpt_cb cur_wpt_cb;
led_cb cur_led_cb;
// Temporary structure storing a write span
struct wspan {
int start, end;
std::vector<int> flux_change_positions;
};
static void wspan_split_on_wrap(std::vector<wspan> &wspans);
static void wspan_remove_damaged(std::vector<wspan> &wspans, const std::vector<uint32_t> &track);
static void wspan_write(const std::vector<wspan> &wspans, std::vector<uint32_t> &track);
void register_formats();
void check_led();
uint32_t find_position(attotime &base, const attotime &when);
int find_index(uint32_t position, const std::vector<uint32_t> &buf) const;
bool test_track_last_entry_warps(const std::vector<uint32_t> &buf) const;
attotime position_to_time(const attotime &base, int position) const;
void write_zone(uint32_t *buf, int &cells, int &index, uint32_t spos, uint32_t epos, uint32_t mg);
void commit_image();
u32 hash32(u32 val) const;

View File

@ -1674,7 +1674,21 @@ bool a2_woz_format::load(util::random_read &io, uint32_t form_factor, const std:
if (r32(img, trks_off + 4) == 0)
return false;
generate_track_from_bitstream(track, head, &img[boff], r32(img, trks_off + 4), image, subtrack, 0xffff);
uint32_t track_size = r32(img, trks_off + 4);
// With 5.25 floppies the end-of-track may be missing
// if unformatted. Accept track length down to 95% of
// 51090, otherwise pad it
bool short_track = !is_35 && track_size < 48535;
if(short_track) {
std::vector<uint8_t> buffer(6387, 0);
memcpy(buffer.data(), &img[boff], (track_size + 7) / 8);
generate_track_from_bitstream(track, head, buffer.data(), 51090, image, subtrack, 0xffff);
} else
generate_track_from_bitstream(track, head, &img[boff], track_size, image, subtrack, 0xffff);
if(is_35 && !track && head)
image->set_variant(r32(img, trks_off + 4) >= 90000 ? floppy_image::DSHD : floppy_image::DSDD);

View File

@ -177,9 +177,7 @@ bool dfi_format::load(util::random_read &io, uint32_t form_factor, const std::ve
#endif
index_count = 0;
//index_polarity = 0;
uint32_t mg = floppy_image::MG_A;
int tpos = 0;
buf[tpos++] = mg;
for(int i=0; i<tsize; i++) {
uint8_t v = data[i];
if((v & 0x7f) == 0x7f) // 0x7F : no transition, but a carry (FF is a board-on-fire error and is checked for above)
@ -202,23 +200,19 @@ bool dfi_format::load(util::random_read &io, uint32_t form_factor, const std::ve
//if (trans_time <= MIN_THRESH) osd_printf_verbose("dfi_dsk: Throwing out short transition of length %d\n", trans_time);
// the normal case: write the transition at the appropriate time
if ((prev_time == 0) || ((trans_time > MIN_THRESH) && (trans_time <= MAX_THRESH))) {
mg = mg == floppy_image::MG_A ? floppy_image::MG_B : floppy_image::MG_A;
buf[tpos++] = mg | uint32_t((200000000ULL*cur_time)/index_time);
buf[tpos++] = floppy_image::MG_F | uint32_t((200000000ULL*cur_time)/index_time);
prev_time = cur_time;
}
// the long case: we probably missed a transition, stuff an extra guessed one in there to see if it helps
if (trans_time > MAX_THRESH) {
mg = mg == floppy_image::MG_A ? floppy_image::MG_B : floppy_image::MG_A;
if (((track%2)==0)&&(head==0)) osd_printf_info("dfi_dsk: missed transition, total time for transition is %d\n",trans_time);
#ifndef FAKETRANS_ONE
buf[tpos++] = mg | uint32_t((200000000ULL*(cur_time-(trans_time/2)))/index_time); // generate imaginary transition at half period
buf[tpos++] = floppy_image::MG_F | uint32_t((200000000ULL*(cur_time-(trans_time/2)))/index_time); // generate imaginary transition at half period
#else
buf[tpos++] = mg | uint32_t((200000000ULL*(cur_time-((trans_time*2)/3)))/index_time);
mg = mg == floppy_image::MG_A ? floppy_image::MG_B : floppy_image::MG_A;
buf[tpos++] = mg | uint32_t((200000000ULL*(cur_time-(trans_time/3)))/index_time);
buf[tpos++] = floppy_image::MG_F | uint32_t((200000000ULL*(cur_time-((trans_time*2)/3)))/index_time);
buf[tpos++] = floppy_image::MG_F | uint32_t((200000000ULL*(cur_time-(trans_time/3)))/index_time);
#endif
mg = mg == floppy_image::MG_A ? floppy_image::MG_B : floppy_image::MG_A;
buf[tpos++] = mg | uint32_t(200000000ULL*cur_time/index_time); // generate transition now
buf[tpos++] = floppy_image::MG_F | uint32_t(200000000ULL*cur_time/index_time); // generate transition now
prev_time = cur_time;
}
}

View File

@ -91,9 +91,10 @@ bool floppy_image::track_is_formatted(int track, int head, int subtrack)
const auto &data = track_array[idx][head].cell_data;
if(data.empty())
return false;
if(data.size() == 1 && (data[0] & MG_MASK) == MG_N)
return false;
return true;
for(uint32_t mg : data)
if((mg & floppy_image::MG_MASK) == floppy_image::MG_F)
return true;
return false;
}
const char *floppy_image::get_variant_name(uint32_t form_factor, uint32_t variant)
@ -840,17 +841,11 @@ void floppy_image_format_t::generate_track(const desc_e *desc, int track, int he
generate_track_from_levels(track, head, buffer, 0, image);
}
void floppy_image_format_t::normalize_times(std::vector<uint32_t> &buffer)
void floppy_image_format_t::normalize_times(std::vector<uint32_t> &buffer, uint32_t last_position)
{
unsigned int total_sum = 0;
for(unsigned int i=0; i != buffer.size(); i++)
total_sum += buffer[i] & floppy_image::TIME_MASK;
unsigned int current_sum = 0;
for(unsigned int i=0; i != buffer.size(); i++) {
uint32_t time = buffer[i] & floppy_image::TIME_MASK;
buffer[i] = (buffer[i] & floppy_image::MG_MASK) | (200000000ULL * current_sum / total_sum);
current_sum += time;
buffer[i] = (buffer[i] & floppy_image::MG_MASK) | (200000000ULL * time / last_position);
}
}
@ -859,36 +854,11 @@ void floppy_image_format_t::generate_track_from_bitstream(int track, int head, c
std::vector<uint32_t> &dest = image->get_buffer(track, head, subtrack);
dest.clear();
// If the bitstream has an odd number of inversions, one needs to be added.
// Put in in the middle of the half window after the center inversion, where
// any fdc ignores it.
int inversions = 0;
for(int i=0; i != track_size; i++)
if(trackbuf[i >> 3] & (0x80 >> (i & 7)))
inversions++;
bool need_flux = inversions & 1;
dest.push_back(floppy_image::MG_F | (i*2+1));
uint32_t cbit = floppy_image::MG_A;
uint32_t count = 0;
for(int i=0; i != track_size; i++)
if(trackbuf[i >> 3] & (0x80 >> (i & 7))) {
dest.push_back(cbit | (count+2));
cbit = cbit == floppy_image::MG_A ? floppy_image::MG_B : floppy_image::MG_A;
if(need_flux) {
need_flux = false;
dest.push_back(cbit | 1);
cbit = cbit == floppy_image::MG_A ? floppy_image::MG_B : floppy_image::MG_A;
count = 1;
} else
count = 2;
} else
count += 4;
if(count)
dest.push_back(cbit | count);
normalize_times(dest);
normalize_times(dest, track_size*2);
if(splice >= 0 || splice < track_size) {
int splpos = uint64_t(200000000) * splice / track_size;
@ -902,81 +872,23 @@ void floppy_image_format_t::generate_track_from_levels(int track, int head, std:
splice_pos = splice_pos % trackbuf.size();
uint32_t splice_angular_pos = trackbuf[splice_pos] & floppy_image::TIME_MASK;
// Check if we need to invert a cell to get an even number of
// transitions on the whole track
//
// Also check if all MG values are valid
int transition_count = 0;
for(auto & elem : trackbuf) {
switch(elem & floppy_image::MG_MASK) {
case MG_1:
transition_count++;
break;
case MG_W:
throw std::runtime_error(util::string_format("Weak bits not yet handled, track %d head %d", track, head));
case MG_0:
case floppy_image::MG_N:
case floppy_image::MG_D:
break;
case floppy_image::MG_A:
case floppy_image::MG_B:
default:
throw std::invalid_argument(util::string_format("Incorrect MG information in generate_track_from_levels, track %d head %d", track, head));
}
}
if(transition_count & 1) {
int pos = splice_pos;
while((trackbuf[pos] & floppy_image::MG_MASK) != MG_0 && (trackbuf[pos] & floppy_image::MG_MASK) != MG_1) {
pos++;
if(pos == int(trackbuf.size()))
pos = 0;
if(pos == splice_pos)
goto meh;
}
if((trackbuf[pos] & floppy_image::MG_MASK) == MG_0)
trackbuf[pos] = (trackbuf[pos] & floppy_image::TIME_MASK) | MG_1;
else
trackbuf[pos] = (trackbuf[pos] & floppy_image::TIME_MASK) | MG_0;
meh:
;
}
// Maximal number of cells which happens when the buffer is all MG_1/MG_N alternated, which would be 3/2
std::vector<uint32_t> &dest = image->get_buffer(track, head);
dest.clear();
uint32_t cbit = floppy_image::MG_A;
uint32_t count = 0;
uint32_t total_time = 0;
for(auto & elem : trackbuf) {
uint32_t bit = elem & floppy_image::MG_MASK;
uint32_t time = elem & floppy_image::TIME_MASK;
if(bit == MG_0) {
count += time;
continue;
}
if(bit == MG_1) {
count += time >> 1;
dest.push_back(cbit | count);
cbit = cbit == floppy_image::MG_A ? floppy_image::MG_B : floppy_image::MG_A;
count = time - (time >> 1);
continue;
}
dest.push_back(cbit | count);
dest.push_back(elem);
count = 0;
if(bit == MG_1)
dest.push_back(floppy_image::MG_F | (total_time + (time >> 1)));
else if(bit != MG_0)
dest.push_back(bit | total_time);
total_time += time;
}
if(count)
dest.push_back(cbit | count);
normalize_times(dest);
normalize_times(dest, total_time);
image->set_write_splice_position(track, head, splice_angular_pos);
}
@ -1390,7 +1302,14 @@ std::vector<bool> floppy_image_format_t::generate_bitstream_from_track(int track
{
std::vector<bool> trackbuf;
std::vector<uint32_t> &tbuf = image->get_buffer(track, head, subtrack);
if(tbuf.size() <= 1) {
bool track_has_info = false;
for(uint32_t mg : tbuf)
if((mg & floppy_image::MG_MASK) == floppy_image::MG_F) {
track_has_info = true;
break;
}
if(!track_has_info) {
// Unformatted track
int track_size = 200000000/cell_size;
trackbuf.resize(track_size, false);
@ -1414,8 +1333,14 @@ std::vector<bool> floppy_image_format_t::generate_bitstream_from_track(int track
uint32_t scanned = 0;
while(scanned < 200000000) {
// Note that all magnetic cell type changes are considered
// edges. No randomness added for neutral/damaged cells
// Note that only MG_F edges are taken into account, the rest is ignored.
// The lack of MG_F has been tested for previously.
while((tbuf[cur_entry] & floppy_image::MG_MASK) != floppy_image::MG_F) {
cur_entry ++;
if(cur_entry == tbuf.size())
cur_entry = 0;
}
int edge = tbuf[cur_entry] & floppy_image::TIME_MASK;
if(edge < cur_pos)
edge += 200000000;
@ -1474,11 +1399,8 @@ std::vector<bool> floppy_image_format_t::generate_bitstream_from_track(int track
// Wrap around
if(cur_entry == int(tbuf.size())-1 &&
(tbuf[cur_entry] & floppy_image::TIME_MASK) < cur_pos) {
// Wrap to index 0 or 1 depending on whether there is a transition exactly at the index hole
cur_entry = (tbuf[int(tbuf.size())-1] & floppy_image::MG_MASK) != (tbuf[0] & floppy_image::MG_MASK) ?
0 : 1;
}
(tbuf[cur_entry] & floppy_image::TIME_MASK) < cur_pos)
cur_entry = 0;
}
return trackbuf;
}

View File

@ -230,8 +230,9 @@ protected:
*/
static void generate_track_from_levels(int track, int head, std::vector<uint32_t> &trackbuf, int splice_pos, floppy_image *image);
//! Normalize the times in a cell buffer to sum up to 200000000
static void normalize_times(std::vector<uint32_t> &buffer);
//! Normalize the times in a cell buffer to bring the
//! 0..last_position range up to 0..200000000
static void normalize_times(std::vector<uint32_t> &buffer, uint32_t last_position);
// Some conversion tables for gcr
static const uint8_t gcr5fw_tb[0x10], gcr5bw_tb[0x20];
@ -450,19 +451,22 @@ private:
//! Internal format is close but not identical to the mfi format.
//!
//!
//! Track data consists of a series of 32-bits lsb-first values
//! representing magnetic cells. Bits 0-27 indicate the absolute
//! position of the start of the cell (not the size), and bits
//! 28-31 the type. Type can be:
//! - 0, MG_A -> Magnetic orientation A
//! - 1, MG_B -> Magnetic orientation B
//! - 2, MG_N -> Non-magnetized zone (neutral)
//! - 3, MG_D -> Damaged zone, reads as neutral but cannot be changed by writing
//! representing the magnetic state. Bits 0-27 indicate the absolute
//! position of encoded event, and bits ! 28-31 the type. Type can be:
//! - 0, MG_F -> Flux orientation change
//! - 1, MG_N -> Start of a non-magnetized zone (neutral)
//! - 2, MG_D -> Start of a damaged zone, reads as neutral but cannot be changed by writing
//! - 3, MG_E -> End of one of the previous zones, *inclusive*
//!
//! The position is in angular units of 1/200,000,000th of a turn.
//! The last cell implicit end position is of course 200,000,000.
//! A N or D zone must not wrap at the 200,000,000 position, it has to
//! be split in two (the first finishing at 199,999,999, the second
//! starting at 0)
//!
//! Unformatted tracks are encoded as zero-size.
//! Unformatted tracks are encoded as zero-size, and are strictly equivalent
//! to (MG_N, 0), (MG_E, 199,999,999)
//!
//! The "track splice" information indicates where to start writing
//! if you try to rewrite a physical disk with the data. Some
@ -491,10 +495,10 @@ public:
TIME_MASK = 0x0fffffff,
MG_MASK = 0xf0000000,
MG_SHIFT = 28, //!< Bitshift constant for magnetic orientation data
MG_A = (0 << MG_SHIFT), //!< - 0, MG_A -> Magnetic orientation A
MG_B = (1 << MG_SHIFT), //!< - 1, MG_B -> Magnetic orientation B
MG_N = (2 << MG_SHIFT), //!< - 2, MG_N -> Non-magnetized zone (neutral)
MG_D = (3 << MG_SHIFT) //!< - 3, MG_D -> Damaged zone, reads as neutral but cannot be changed by writing
MG_F = (0 << MG_SHIFT), //!< - 0, MG_F -> Flux orientation change
MG_N = (1 << MG_SHIFT), //!< - 1, MG_N -> Non-magnetized zone (neutral)
MG_D = (2 << MG_SHIFT), //!< - 2, MG_D -> Damaged zone, reads as neutral but cannot be changed by writing
MG_E = (3 << MG_SHIFT) //!< - 3, MG_E -> End of zone
};

View File

@ -316,17 +316,11 @@ void hfe_format::generate_track_from_hfe_bitstream(int cyl, int head, int sample
// HFE does not define subtracks; set to 0
// MG_1 / MG_0 are (logical) levels that indicate transition / no change
// MG_A / MG_B are physical flux directions
//
// Cell: | AAAABBBB | = MG_1 = | BBBBAAAA |
// | AAAAAAAA | = MG_0 = | BBBBBBBB |
// MG_F is the position of a flux transition
std::vector<uint32_t> &dest = image->get_buffer(cyl, head, 0);
dest.clear();
// Start with MG_A
uint32_t cbit = floppy_image::MG_A;
int offset = 0x100;
if (head==0)
@ -338,10 +332,6 @@ void hfe_format::generate_track_from_hfe_bitstream(int cyl, int head, int sample
uint8_t current = 0;
int time = 0;
dest.push_back(cbit | time);
cbit = floppy_image::MG_B;
// Oversampled FM images (250 kbit/s) start with a 0, where a 1 is
// expected for 125 kbit/s.
// In order to make an oversampled image look like a normally sampled one,
@ -384,13 +374,8 @@ void hfe_format::generate_track_from_hfe_bitstream(int cyl, int head, int sample
{
time += samplelength;
if ((current & 1)!=0)
{
// Append another transition to the vector
dest.push_back(cbit | time);
// Toggle the cell level
cbit = (cbit == floppy_image::MG_A)? floppy_image::MG_B : floppy_image::MG_A;
}
dest.push_back(floppy_image::MG_F | time);
// HFE uses little-endian bit order
current >>= 1;

View File

@ -7,6 +7,7 @@
#include <zlib.h>
#include <cstring>
#include <functional>
/*
@ -31,31 +32,27 @@
simple "compress" function.
Track data consists of a series of 32-bits lsb-first values
representing magnetic cells. Bits 0-27 indicate the sizes, and bits
28-31 the types. Type can be:
- 0, MG_A -> Magnetic orientation A
- 1, MG_B -> Magnetic orientation B
- 2, MG_N -> Non-magnetized zone (neutral)
- 3, MG_D -> Damaged zone, reads as neutral but cannot be changed by writing
representing magnetic cells. Bits 0-27 indicate the position,
delta-packed (e.g. difference with the previous position, starts at
0), and bits 28-31 the types. Type can be:
Remember that the fdcs detect transitions, not absolute levels, so
the actual physical significance of the orientation A and B is
arbitrary.
- 0, MG_F -> Flux orientation change
- 1, MG_N -> Non-magnetized zone (neutral)
- 2, MG_D -> Damaged zone, reads as neutral but cannot be changed by writing
- 3, MG_E -> End of zone
Tracks data is aligned so that the index pulse is at the start,
whether the disk is hard-sectored or not.
The size is the angular size in units of 1/200,000,000th of a turn.
Such a size, not coincidentally at all, is also the flyover time in
nanoseconds for a perfectly stable 300rpm drive. That makes the
standard cell size of a MFM 3.5" DD floppy at 2000 exactly for
instance (2us). Smallest expected cell size is 500 (ED density
drives).
The position is the angular position in units of 1/200,000,000th of
a turn. A size in such units, not coincidentally at all, is also
the flyover time in nanoseconds for a perfectly stable 300rpm drive.
That makes the standard cell size of a MFM 3.5" DD floppy at 2000
exactly for instance (2us). Smallest expected cell size is 500 (ED
density drives).
The sum of all sizes must of course be 200,000,000.
An unformatted track is equivalent to one big MG_N cell covering a
whole turn, but is encoded as zero-size.
An unformatted track is equivalent to a pair (MG_N, 0), (MG_E,
199999999) but is encoded as zero-size.
The "track splice" information indicates where to start writing
if you try to rewrite a physical disk with the data. Some
@ -72,7 +69,8 @@
TODO: big-endian support
*/
const char mfi_format::sign[16] = "MESSFLOPPYIMAGE"; // Includes the final \0
const char mfi_format::sign_old[16] = "MESSFLOPPYIMAGE"; // Includes the final \0
const char mfi_format::sign[16] = "MAMEFLOPPYIMAGE"; // Includes the final \0
mfi_format::mfi_format() : floppy_image_format_t()
{
@ -85,7 +83,7 @@ const char *mfi_format::name() const
const char *mfi_format::description() const
{
return "MESS floppy image";
return "MAME floppy image";
}
const char *mfi_format::extensions() const
@ -104,7 +102,7 @@ int mfi_format::identify(util::random_read &io, uint32_t form_factor, const std:
size_t actual;
io.read_at(0, &h, sizeof(header), actual);
if(memcmp( h.sign, sign, 16 ) == 0 &&
if((memcmp( h.sign, sign, 16) == 0 || memcmp( h.sign, sign_old, 16) == 0) &&
(h.cyl_count & CYLINDER_MASK) <= 84 &&
(h.cyl_count >> RESOLUTION_SHIFT) < 3 &&
h.head_count <= 2 &&
@ -129,7 +127,46 @@ bool mfi_format::load(util::random_read &io, uint32_t form_factor, const std::ve
if(!h.cyl_count)
return true;
std::function<void (const std::vector<uint32_t> &src, std::vector<uint32_t> &track)> converter;
if(!memcmp( h.sign, sign, 16)) {
converter = [](const std::vector<uint32_t> &src, std::vector<uint32_t> &track) -> void {
uint32_t ctime = 0;
for(uint32_t mg : src) {
ctime += mg & TIME_MASK;
track.push_back((mg & MG_MASK) | ctime);
}
};
} else {
converter = [](const std::vector<uint32_t> &src, std::vector<uint32_t> &track) -> void {
unsigned int cell_count = src.size();
uint32_t mg = src[0] & MG_MASK;
uint32_t wmg = src[cell_count - 1] & MG_MASK;
if(mg != wmg && (mg == OLD_MG_A || mg == OLD_MG_B) && (wmg == OLD_MG_A && wmg == OLD_MG_B))
// Flux change at 0, add it
track.push_back(MG_F | 0);
uint32_t ctime = 0;
for(unsigned int i=0; i != cell_count; i++) {
uint32_t nmg = src[i] & MG_MASK;
if(nmg == OLD_MG_N || nmg == OLD_MG_D) {
track.push_back((nmg == OLD_MG_N ? MG_N : MG_D) | ctime);
ctime += src[i] & TIME_MASK;
track.push_back(MG_E | (ctime-1));
nmg = 0xffffffff;
} else {
if(mg != 0xffffffff && mg != nmg)
track.push_back(MG_F | ctime);
ctime += src[i] & TIME_MASK;
}
mg = nmg;
}
};
}
std::vector<uint8_t> compressed;
std::vector<uint32_t> uncompressed;
entry *ent = entries;
for(unsigned int cyl=0; cyl <= (h.cyl_count - 1) << 2; cyl += 4 >> resolution)
@ -143,31 +180,22 @@ bool mfi_format::load(util::random_read &io, uint32_t form_factor, const std::ve
continue;
}
unsigned int cell_count = ent->uncompressed_size/4;
compressed.resize(ent->compressed_size);
uncompressed.resize(cell_count);
io.read_at(ent->offset, &compressed[0], ent->compressed_size, actual);
unsigned int cell_count = ent->uncompressed_size/4;
std::vector<uint32_t> &trackbuf = image->get_buffer(cyl >> 2, head, cyl & 3);;
trackbuf.resize(cell_count);
uLongf size = ent->uncompressed_size;
if(uncompress((Bytef *)&trackbuf[0], &size, &compressed[0], ent->compressed_size) != Z_OK) {
if(uncompress((Bytef *)uncompressed.data(), &size, &compressed[0], ent->compressed_size) != Z_OK) {
fprintf(stderr, "fail1\n");
return false;
}
uint32_t cur_time = 0;
for(unsigned int i=0; i != cell_count; i++) {
uint32_t next_cur_time = cur_time + (trackbuf[i] & TIME_MASK);
trackbuf[i] = (trackbuf[i] & MG_MASK) | cur_time;
cur_time = next_cur_time;
}
if(cur_time != 200000000) {
fprintf(stderr, "fail2 %d\n", cur_time);
return false;
}
std::vector<uint32_t> &trackbuf = image->get_buffer(cyl >> 2, head, cyl & 3);;
trackbuf.clear();
converter(uncompressed, trackbuf);
ent++;
}
@ -214,13 +242,11 @@ bool mfi_format::save(util::random_read_write &io, const std::vector<uint32_t> &
continue;
}
memcpy(&precomp[0], &buffer[0], tsize*4);
for(int j=0; j<tsize-1; j++)
precomp[j] = (precomp[j] & floppy_image::MG_MASK) |
((precomp[j+1] & floppy_image::TIME_MASK) -
(precomp[j] & floppy_image::TIME_MASK));
precomp[tsize-1] = (precomp[tsize-1] & floppy_image::MG_MASK) |
(200000000 - (precomp[tsize-1] & floppy_image::TIME_MASK));
uint32_t ctime = 0;
for(int i=0; i != tsize; i++) {
precomp[i] = (buffer[i] & MG_MASK) | ((buffer[i] & TIME_MASK) - ctime);
ctime = buffer[i] & TIME_MASK;
}
uLongf csize = max_track_size*4 + 1000;
if(compress(postcomp.get(), &csize, (const Bytef *)precomp.get(), tsize*4) != Z_OK)

View File

@ -27,15 +27,21 @@ private:
MG_MASK = 0xf0000000,
MG_SHIFT = 28,
MG_A = (0 << MG_SHIFT),
MG_B = (1 << MG_SHIFT),
MG_N = (2 << MG_SHIFT),
MG_D = (3 << MG_SHIFT),
OLD_MG_A = (0 << MG_SHIFT),
OLD_MG_B = (1 << MG_SHIFT),
OLD_MG_N = (2 << MG_SHIFT),
OLD_MG_D = (3 << MG_SHIFT),
MG_F = (0 << MG_SHIFT), //!< - 0, MG_F -> Flux orientation change
MG_N = (1 << MG_SHIFT), //!< - 1, MG_N -> Non-magnetized zone (neutral)
MG_D = (2 << MG_SHIFT), //!< - 2, MG_D -> Damaged zone, reads as neutral but cannot be changed by writing
MG_E = (3 << MG_SHIFT), //!< - 3, MG_E -> End of zone
RESOLUTION_SHIFT = 30,
CYLINDER_MASK = 0x3fffffff
};
static const char sign_old[16];
static const char sign[16];
struct header {

View File

@ -17,23 +17,20 @@
// One = | 2237 | 6950 |
// 0.5us ~= 143
void vtech_common_format::wbit(std::vector<uint32_t> &buffer, uint32_t &pos, uint32_t &mg, bool bit)
void vtech_common_format::wbit(std::vector<uint32_t> &buffer, uint32_t &pos, bool bit)
{
buffer.push_back(pos | mg);
mg = mg == floppy_image::MG_A ? floppy_image::MG_B : floppy_image::MG_A;
if(bit) {
pos += 2237;
buffer.push_back(pos | mg);
mg = mg == floppy_image::MG_A ? floppy_image::MG_B : floppy_image::MG_A;
buffer.push_back(pos | floppy_image::MG_F);
pos += 6950;
} else
pos += 9187;
}
void vtech_common_format::wbyte(std::vector<uint32_t> &buffer, uint32_t &pos, uint32_t &mg, uint8_t byte)
void vtech_common_format::wbyte(std::vector<uint32_t> &buffer, uint32_t &pos, uint8_t byte)
{
for(int i = 7; i >= 0; i--)
wbit(buffer, pos, mg, (byte >> i) & 1);
wbit(buffer, pos, (byte >> i) & 1);
}
void vtech_common_format::image_to_flux(const std::vector<uint8_t> &bdata, floppy_image *image)
@ -44,40 +41,39 @@ void vtech_common_format::image_to_flux(const std::vector<uint8_t> &bdata, flopp
for(int track = 0; track != 40; track ++) {
uint32_t pos = 0;
uint32_t mg = floppy_image::MG_A;
std::vector<uint32_t> &buffer = image->get_buffer(track, 0);
buffer.clear();
image->set_write_splice_position(track, 0, 0);
// One window of pad at the start to avoid problems with the write splice
wbit(buffer, pos, mg, 0);
wbit(buffer, pos, 0);
for(int sector = 0; sector != 16; sector ++) {
uint8_t sid = sector_map[sector];
for(int i=0; i != 7; i++)
wbyte(buffer, pos, mg, 0x80);
wbyte(buffer, pos, mg, 0x00);
wbyte(buffer, pos, mg, 0xfe);
wbyte(buffer, pos, mg, 0xe7);
wbyte(buffer, pos, mg, 0x18);
wbyte(buffer, pos, mg, 0xc3);
wbyte(buffer, pos, mg, track);
wbyte(buffer, pos, mg, sid);
wbyte(buffer, pos, mg, track+sid);
wbyte(buffer, pos, 0x80);
wbyte(buffer, pos, 0x00);
wbyte(buffer, pos, 0xfe);
wbyte(buffer, pos, 0xe7);
wbyte(buffer, pos, 0x18);
wbyte(buffer, pos, 0xc3);
wbyte(buffer, pos, track);
wbyte(buffer, pos, sid);
wbyte(buffer, pos, track+sid);
for(int i=0; i != 5; i++)
wbyte(buffer, pos, mg, 0x80);
wbyte(buffer, pos, mg, 0x00);
wbyte(buffer, pos, mg, 0xc3);
wbyte(buffer, pos, mg, 0x18);
wbyte(buffer, pos, mg, 0xe7);
wbyte(buffer, pos, mg, 0xfe);
wbyte(buffer, pos, 0x80);
wbyte(buffer, pos, 0x00);
wbyte(buffer, pos, 0xc3);
wbyte(buffer, pos, 0x18);
wbyte(buffer, pos, 0xe7);
wbyte(buffer, pos, 0xfe);
uint16_t chk = 0;
const uint8_t *src = bdata.data() + 16*128*track + 128*sid;
for(int i=0; i != 128; i++) {
chk += src[i];
wbyte(buffer, pos, mg, src[i]);
wbyte(buffer, pos, src[i]);
}
wbyte(buffer, pos, mg, chk);
wbyte(buffer, pos, mg, chk >> 8);
wbyte(buffer, pos, chk);
wbyte(buffer, pos, chk >> 8);
}
// Rest is just not formatted
buffer.push_back(pos | floppy_image::MG_N);
@ -95,29 +91,37 @@ std::vector<uint8_t> vtech_common_format::flux_to_image(floppy_image *image)
continue;
std::vector<bool> bitstream;
int lpos = -1;
bool looped = !((buffer[sz-1] ^ buffer[0]) & floppy_image::MG_MASK);
int cpos = looped ? sz-1 : 0;
while(cpos != lpos) {
int dt = looped && cpos == sz-1 ? (200000000 - (buffer[cpos] & floppy_image::TIME_MASK)) + (buffer[1] & floppy_image::TIME_MASK) :
cpos == sz-1 ? 200000000 - (buffer[cpos] & floppy_image::TIME_MASK) :
(buffer[cpos+1] & floppy_image::TIME_MASK) - (buffer[cpos] & floppy_image::TIME_MASK);
int t = dt >= 9187 - 143 ? 0 :
dt >= 2237 - 143 && dt <= 2237 + 143 ? 1 :
2;
if(t <= 1) {
if(lpos == -1)
lpos = cpos;
bitstream.push_back(t);
int cpos = 0;
while((buffer[cpos] & floppy_image::MG_MASK) != floppy_image::MG_F) {
cpos++;
if(cpos == sz) {
cpos = -1;
break;
}
cpos += 1;
if(cpos == sz)
cpos = looped ? 1 : 0;
}
if(cpos == -1)
continue;
for(;;) {
int npos = cpos;
for(;;) {
npos ++;
if(npos == sz)
npos = 0;
if((buffer[npos] & floppy_image::MG_MASK) == floppy_image::MG_F)
break;
}
int dt = (buffer[npos] & floppy_image::TIME_MASK) - (buffer[cpos] & floppy_image::TIME_MASK);
if(dt < 0)
cpos += 200000000;
bitstream.push_back(dt < 9187 - 143);
if(npos <= cpos)
break;
cpos = npos;
}
int mode = 0;
looped = false;
int pos = 0;
int count = 0;
bool looped = false;
uint8_t *dest = nullptr;
[[maybe_unused]] uint16_t checksum = 0;
uint64_t buf = 0;

View File

@ -22,8 +22,8 @@ protected:
static void image_to_flux(const std::vector<uint8_t> &bdata, floppy_image *image);
static std::vector<uint8_t> flux_to_image(floppy_image *image);
static void wbit(std::vector<uint32_t> &buffer, uint32_t &pos, uint32_t &mg, bool bit);
static void wbyte(std::vector<uint32_t> &buffer, uint32_t &pos, uint32_t &mg, uint8_t byte);
static void wbit(std::vector<uint32_t> &buffer, uint32_t &pos, bool bit);
static void wbyte(std::vector<uint32_t> &buffer, uint32_t &pos, uint8_t byte);
};
class vtech_bin_format : public vtech_common_format {