summaryrefslogtreecommitdiffstatshomepage
path: root/src/lib/formats/hti_tape.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib/formats/hti_tape.cpp')
-rw-r--r--src/lib/formats/hti_tape.cpp192
1 files changed, 104 insertions, 88 deletions
diff --git a/src/lib/formats/hti_tape.cpp b/src/lib/formats/hti_tape.cpp
index 29bab6cd51c..7d6119860f1 100644
--- a/src/lib/formats/hti_tape.cpp
+++ b/src/lib/formats/hti_tape.cpp
@@ -8,11 +8,15 @@
#include "hti_tape.h"
-#include "imageutl.h"
+#include "ioprocs.h"
+#include "multibyte.h"
+
+#include <tuple>
static constexpr uint32_t OLD_FILE_MAGIC = 0x5441434f; // Magic value at start of old-format image file: "TACO"
-static constexpr uint32_t FILE_MAGIC = 0x48544930; // Magic value at start of image file: "HTI0"
+static constexpr uint32_t FILE_MAGIC_DELTA = 0x48544930; // Magic value at start of delta-modulation image file: "HTI0"
+static constexpr uint32_t FILE_MAGIC_MANCHESTER = 0x48544931; // Magic value at start of manchester-modulation image file: "HTI1"
// *** Position of tape holes ***
// At beginning of tape:
@@ -41,25 +45,32 @@ static const hti_format_t::tape_pos_t tape_holes[] = {
};
hti_format_t::hti_format_t()
- : m_bits_per_word(16)
+ : m_img_format(HTI_DELTA_MOD_16_BITS)
{
clear_tape();
}
-bool hti_format_t::load_tape(io_generic *io)
+bool hti_format_t::load_tape(util::random_read &io)
{
- uint8_t tmp[ 4 ];
+ if (io.seek(0, SEEK_SET)) {
+ return false;
+ }
- io_generic_read(io, tmp, 0, 4);
- auto magic = pick_integer_be(tmp , 0 , 4);
- if (magic != FILE_MAGIC && magic != OLD_FILE_MAGIC) {
+ uint8_t tmp[4];
+ auto const [err, actual] = read(io, tmp, 4);
+ if (err || (4 != actual)) {
return false;
}
- uint64_t offset = 4;
+ auto magic = get_u32be(tmp);
+ if (((m_img_format == HTI_DELTA_MOD_16_BITS || m_img_format == HTI_DELTA_MOD_17_BITS) && magic != FILE_MAGIC_DELTA && magic != OLD_FILE_MAGIC) ||
+ (m_img_format == HTI_MANCHESTER_MOD && magic != FILE_MAGIC_MANCHESTER)) {
+ return false;
+ }
- for (tape_track_t& track : m_tracks) {
- if (!load_track(io , offset , track , magic == OLD_FILE_MAGIC)) {
+ for (unsigned i = 0; i < no_of_tracks(); i++) {
+ tape_track_t &track = m_tracks[i];
+ if (!load_track(io, track, magic == OLD_FILE_MAGIC)) {
clear_tape();
return false;
}
@@ -68,46 +79,46 @@ bool hti_format_t::load_tape(io_generic *io)
return true;
}
-void hti_format_t::save_tape(io_generic *io)
+void hti_format_t::save_tape(util::random_read_write &io)
{
- uint8_t tmp[ 4 ];
+ io.seek(0, SEEK_SET); // FIXME: check for errors
- place_integer_be(tmp, 0, 4, FILE_MAGIC);
- io_generic_write(io, tmp, 0, 4);
+ uint8_t tmp[4];
- uint64_t offset = 4;
+ put_u32be(tmp, m_img_format == HTI_MANCHESTER_MOD ? FILE_MAGIC_MANCHESTER : FILE_MAGIC_DELTA);
+ write(io, tmp, 4); // FIXME: check for errors
- for (const tape_track_t& track : m_tracks) {
- tape_pos_t next_pos = (tape_pos_t)-1;
+ for (unsigned i = 0; i < no_of_tracks(); i++) {
+ const tape_track_t &track = m_tracks[i];
+ tape_pos_t next_pos = tape_pos_t(-1);
unsigned n_words = 0;
tape_track_t::const_iterator it_start;
for (tape_track_t::const_iterator it = track.cbegin(); it != track.cend(); ++it) {
if (it->first != next_pos) {
- dump_sequence(io , offset , it_start , n_words);
+ dump_sequence(io, it_start, n_words);
it_start = it;
n_words = 0;
}
next_pos = it->first + word_length(it->second);
n_words++;
}
- dump_sequence(io , offset , it_start , n_words);
+ dump_sequence(io, it_start, n_words);
// End of track
- place_integer_le(tmp, 0, 4, (uint32_t)-1);
- io_generic_write(io, tmp, offset, 4);
- offset += 4;
+ put_u32le(tmp, (uint32_t)-1);
+ write(io, tmp, 4); // FIXME: check for errors
}
}
void hti_format_t::clear_tape()
{
- for (tape_track_t& track : m_tracks) {
+ for (tape_track_t &track : m_tracks) {
track.clear();
}
}
-hti_format_t::tape_pos_t hti_format_t::word_length(tape_word_t w)
+hti_format_t::tape_pos_t hti_format_t::word_length(tape_word_t w) const
{
- unsigned zeros , ones;
+ unsigned zeros, ones;
// pop count of w
ones = (w & 0x5555) + ((w >> 1) & 0x5555);
@@ -117,10 +128,10 @@ hti_format_t::tape_pos_t hti_format_t::word_length(tape_word_t w)
zeros = 16 - ones;
- return zeros * ZERO_BIT_LEN + ones * ONE_BIT_LEN;
+ return zeros * bit_length(false) + ones * bit_length(true);
}
-hti_format_t::tape_pos_t hti_format_t::farthest_end(const track_iterator_t& it , bool forward)
+hti_format_t::tape_pos_t hti_format_t::farthest_end(const track_iterator_t &it, bool forward) const
{
if (forward) {
return word_end_pos(it);
@@ -129,7 +140,7 @@ hti_format_t::tape_pos_t hti_format_t::farthest_end(const track_iterator_t& it ,
}
}
-bool hti_format_t::pos_offset(tape_pos_t& pos , bool forward , tape_pos_t offset)
+bool hti_format_t::pos_offset(tape_pos_t &pos, bool forward, tape_pos_t offset)
{
if (offset == 0) {
return true;
@@ -153,7 +164,7 @@ bool hti_format_t::pos_offset(tape_pos_t& pos , bool forward , tape_pos_t offset
}
}
-hti_format_t::tape_pos_t hti_format_t::next_hole(tape_pos_t pos , bool forward)
+hti_format_t::tape_pos_t hti_format_t::next_hole(tape_pos_t pos, bool forward)
{
if (forward) {
for (tape_pos_t hole : tape_holes) {
@@ -164,9 +175,9 @@ hti_format_t::tape_pos_t hti_format_t::next_hole(tape_pos_t pos , bool forward)
// No more holes: will hit end of tape
return NULL_TAPE_POS;
} else {
- for (int i = (sizeof(tape_holes) / sizeof(tape_holes[ 0 ])) - 1; i >= 0; i--) {
- if (tape_holes[ i ] < pos) {
- return tape_holes[ i ];
+ for (int i = (sizeof(tape_holes) / sizeof(tape_holes[0])) - 1; i >= 0; i--) {
+ if (tape_holes[i] < pos) {
+ return tape_holes[i];
}
}
// No more holes: will hit start of tape
@@ -174,45 +185,45 @@ hti_format_t::tape_pos_t hti_format_t::next_hole(tape_pos_t pos , bool forward)
}
}
-void hti_format_t::write_word(unsigned track_no , tape_pos_t start , tape_word_t word , tape_pos_t& length , bool forward)
+void hti_format_t::write_word(unsigned track_no, tape_pos_t start, tape_word_t word, tape_pos_t &length, bool forward)
{
- tape_track_t& track = m_tracks[ track_no ];
+ tape_track_t &track = m_tracks[track_no];
track_iterator_t it_low = track.lower_bound(start);
- adjust_it(track , it_low , start);
+ adjust_it(track, it_low, start);
length = word_length(word);
tape_pos_t end_pos = start + length;
track_iterator_t it_high = track.lower_bound(end_pos);
- track.erase(it_low , it_high);
+ track.erase(it_low, it_high);
// A 0 word is inserted after the word being written, if space allows.
// This is meant to avoid fragmentation of the slack space at the end of a record
// as the record expands & contracts when re-written with different content.
// Without this fix, a gap could form in the slack big enough to cause
// false gap detections.
- if (forward && it_high != track.end() && (it_high->first - end_pos) >= (ZERO_BIT_LEN * 16)) {
+ if (forward && it_high != track.end() && (it_high->first - end_pos) >= (bit_length(false) * 16)) {
track.insert(it_high, std::make_pair(end_pos, 0));
it_high--;
}
- track.insert(it_high , std::make_pair(start, word));
+ track.insert(it_high, std::make_pair(start, word));
}
-void hti_format_t::write_gap(unsigned track_no , tape_pos_t a , tape_pos_t b)
+void hti_format_t::write_gap(unsigned track_no, tape_pos_t a, tape_pos_t b)
{
- ensure_a_lt_b(a , b);
- tape_track_t& track = m_tracks[ track_no ];
+ ensure_a_lt_b(a, b);
+ tape_track_t &track = m_tracks[track_no];
track_iterator_t it_low = track.lower_bound(a);
- adjust_it(track , it_low , a);
+ adjust_it(track, it_low, a);
track_iterator_t it_high = track.lower_bound(b);
track.erase(it_low, it_high);
}
-bool hti_format_t::just_gap(unsigned track_no , tape_pos_t a , tape_pos_t b)
+bool hti_format_t::just_gap(unsigned track_no, tape_pos_t a, tape_pos_t b)
{
- ensure_a_lt_b(a , b);
- tape_track_t& track = m_tracks[ track_no ];
+ ensure_a_lt_b(a, b);
+ tape_track_t &track = m_tracks[track_no];
track_iterator_t it_low = track.lower_bound(a);
track_iterator_t it_high = track.lower_bound(b);
@@ -221,9 +232,9 @@ bool hti_format_t::just_gap(unsigned track_no , tape_pos_t a , tape_pos_t b)
return it_low == it_high;
}
-bool hti_format_t::next_data(unsigned track_no , tape_pos_t pos , bool forward , bool inclusive , track_iterator_t& it)
+bool hti_format_t::next_data(unsigned track_no, tape_pos_t pos, bool forward, bool inclusive, track_iterator_t &it)
{
- tape_track_t& track = m_tracks[ track_no ];
+ tape_track_t &track = m_tracks[track_no];
it = track.lower_bound(pos);
if (forward) {
if (inclusive) {
@@ -243,9 +254,9 @@ bool hti_format_t::next_data(unsigned track_no , tape_pos_t pos , bool forward ,
}
}
-hti_format_t::adv_res_t hti_format_t::adv_it(unsigned track_no , bool forward , track_iterator_t& it)
+hti_format_t::adv_res_t hti_format_t::adv_it(unsigned track_no, bool forward, track_iterator_t &it)
{
- tape_track_t& track = m_tracks[ track_no ];
+ tape_track_t &track = m_tracks[track_no];
if (forward) {
tape_pos_t prev_pos = word_end_pos(it);
++it;
@@ -267,7 +278,7 @@ hti_format_t::adv_res_t hti_format_t::adv_it(unsigned track_no , bool forward ,
}
}
-bool hti_format_t::sync_with_record(unsigned track_no , track_iterator_t& it , unsigned& bit_idx)
+bool hti_format_t::sync_with_record(unsigned track_no, track_iterator_t &it, unsigned &bit_idx)
{
while ((it->second & (1U << bit_idx)) == 0) {
if (bit_idx) {
@@ -288,7 +299,7 @@ bool hti_format_t::sync_with_record(unsigned track_no , track_iterator_t& it , u
return true;
}
-hti_format_t::adv_res_t hti_format_t::next_word(unsigned track_no , track_iterator_t& it , unsigned& bit_idx , tape_word_t& word)
+hti_format_t::adv_res_t hti_format_t::next_word(unsigned track_no, track_iterator_t &it, unsigned &bit_idx, tape_word_t &word)
{
if (bit_idx == 15) {
auto res = adv_it(track_no, true, it);
@@ -311,13 +322,13 @@ hti_format_t::adv_res_t hti_format_t::next_word(unsigned track_no , track_iterat
}
}
-bool hti_format_t::next_gap(unsigned track_no , tape_pos_t& pos , bool forward , tape_pos_t min_gap)
+bool hti_format_t::next_gap(unsigned track_no, tape_pos_t &pos, bool forward, tape_pos_t min_gap)
{
tape_track_t::iterator it;
// First align with next data
- next_data(track_no , pos , forward , true , it);
+ next_data(track_no, pos, forward, true, it);
// Then scan for 1st gap
- tape_track_t& track = m_tracks[ track_no ];
+ tape_track_t &track = m_tracks[track_no];
bool done = false;
track_iterator_t prev_it;
unsigned n_gaps = 1;
@@ -338,7 +349,7 @@ bool hti_format_t::next_gap(unsigned track_no , tape_pos_t& pos , bool forward ,
adv_res_t adv_res;
do {
prev_it = it;
- adv_res = adv_it(track_no , forward , it);
+ adv_res = adv_it(track_no, forward, it);
} while (adv_res == ADV_CONT_DATA);
pos = word_end_pos(prev_it);
}
@@ -358,33 +369,37 @@ bool hti_format_t::next_gap(unsigned track_no , tape_pos_t& pos , bool forward ,
adv_res_t adv_res;
do {
prev_it = it;
- adv_res = adv_it(track_no , forward , it);
+ adv_res = adv_it(track_no, forward, it);
} while (adv_res == ADV_CONT_DATA);
pos = prev_it->first;
}
}
// Set "pos" where minimum gap size is met
- pos_offset(pos , forward , min_gap);
+ pos_offset(pos, forward, min_gap);
return n_gaps == 0;
}
-bool hti_format_t::load_track(io_generic *io , uint64_t& offset , tape_track_t& track , bool old_format)
+bool hti_format_t::load_track(util::random_read &io, tape_track_t &track, bool old_format)
{
- uint8_t tmp[ 4 ];
- uint32_t tmp32;
tape_pos_t delta_pos = 0;
tape_pos_t last_word_end = 0;
track.clear();
while (1) {
- // Read no. of words to follow
- io_generic_read(io, tmp, offset, 4);
- offset += 4;
+ std::error_condition err;
+ size_t actual;
+ uint8_t tmp[4];
+ uint32_t tmp32;
- tmp32 = pick_integer_le(tmp, 0, 4);
+ // Read no. of words to follow
+ std::tie(err, actual) = read(io, tmp, 4);
+ if (err || (4 != actual)) {
+ return false;
+ }
+ tmp32 = get_u32le(tmp);
// Track ends
if (tmp32 == (uint32_t)-1) {
@@ -394,10 +409,11 @@ bool hti_format_t::load_track(io_generic *io , uint64_t& offset , tape_track_t&
unsigned n_words = tmp32;
// Read tape position of block
- io_generic_read(io, tmp, offset, 4);
- offset += 4;
-
- tmp32 = pick_integer_le(tmp, 0, 4);
+ std::tie(err, actual) = read(io, tmp, 4);
+ if (err || (4 != actual)) {
+ return false;
+ }
+ tmp32 = get_u32le(tmp);
tape_pos_t pos = (tape_pos_t)tmp32 + delta_pos;
@@ -407,14 +423,16 @@ bool hti_format_t::load_track(io_generic *io , uint64_t& offset , tape_track_t&
for (unsigned i = 0; i < n_words; i++) {
uint16_t tmp16;
- io_generic_read(io, tmp, offset, 2);
- offset += 2;
- tmp16 = pick_integer_le(tmp, 0, 2);
+ std::tie(err, actual) = read(io, tmp, 2);
+ if (err || (2 != actual)) {
+ return false;
+ }
+ tmp16 = get_u16le(tmp);
if (!old_format) {
- track.insert(std::make_pair(pos , tmp16));
+ track.insert(std::make_pair(pos, tmp16));
pos += word_length(tmp16);
- } else if (m_bits_per_word == 16) {
+ } else if (m_img_format == HTI_DELTA_MOD_16_BITS) {
// Convert HP9845 & HP85 old format
// Basically, in old format each word had 17 bits (an implicit 1
// was added at the end). In new format we just keep the 16 bits
@@ -429,7 +447,7 @@ bool hti_format_t::load_track(io_generic *io , uint64_t& offset , tape_track_t&
track.insert(std::make_pair(pos, tmp16));
pos += word_length(tmp16);
last_word_end = pos;
- delta_pos -= ONE_BIT_LEN;
+ delta_pos -= DELTA_ONE_BIT_LEN;
} else {
// Convert HP9825 old format
// In moving from old to new format we make the 17th bit at the
@@ -458,37 +476,35 @@ bool hti_format_t::load_track(io_generic *io , uint64_t& offset , tape_track_t&
}
if (bits_in_accum) {
track.insert(std::make_pair(pos, word_accum));
- tape_pos_t shift = (tape_pos_t)(16 - bits_in_accum) * ZERO_BIT_LEN;
+ tape_pos_t shift = (tape_pos_t)(16 - bits_in_accum) * DELTA_ZERO_BIT_LEN;
delta_pos += shift;
last_word_end = pos + word_length(word_accum);
}
}
}
-void hti_format_t::dump_sequence(io_generic *io , uint64_t& offset , tape_track_t::const_iterator it_start , unsigned n_words)
+void hti_format_t::dump_sequence(util::random_read_write &io, tape_track_t::const_iterator it_start, unsigned n_words)
{
if (n_words) {
- uint8_t tmp[ 8 ];
- place_integer_le(tmp, 0, 4, n_words);
- place_integer_le(tmp, 4, 4, it_start->first);
- io_generic_write(io, tmp, offset, 8);
- offset += 8;
+ uint8_t tmp[8];
+ put_u32le(&tmp[0], n_words);
+ put_u32le(&tmp[4], it_start->first);
+ write(io, tmp, 8); // FIXME: check for errors
for (unsigned i = 0; i < n_words; i++) {
- place_integer_le(tmp, 0, 2, it_start->second);
- io_generic_write(io, tmp, offset, 2);
- offset += 2;
+ put_u16le(tmp, it_start->second);
+ write(io, tmp, 2); // FIXME: check for errors
++it_start;
}
}
}
-hti_format_t::tape_pos_t hti_format_t::word_end_pos(const track_iterator_t& it)
+hti_format_t::tape_pos_t hti_format_t::word_end_pos(const track_iterator_t &it) const
{
return it->first + word_length(it->second);
}
-void hti_format_t::adjust_it(tape_track_t& track , track_iterator_t& it , tape_pos_t pos)
+void hti_format_t::adjust_it(tape_track_t &track, track_iterator_t &it, tape_pos_t pos) const
{
if (it != track.begin()) {
--it;
@@ -498,7 +514,7 @@ void hti_format_t::adjust_it(tape_track_t& track , track_iterator_t& it , tape_p
}
}
-void hti_format_t::ensure_a_lt_b(tape_pos_t& a , tape_pos_t& b)
+void hti_format_t::ensure_a_lt_b(tape_pos_t &a, tape_pos_t &b)
{
if (a > b) {
// Ensure A always comes before B