summaryrefslogtreecommitdiffstatshomepage
path: root/src/lib
diff options
context:
space:
mode:
author fulivi <fulivi@users.noreply.github.com>2017-07-26 16:02:31 +0200
committer fulivi <fulivi@users.noreply.github.com>2017-08-16 10:38:22 +0200
commit480f865bcfae940f76cb20581313f6ba39054dcf (patch)
treef1bfbcd2d1b1452801f6c5a8ffb6f60585c3ce54 /src/lib
parent4e37ba29bc7d8043209af09d890a5a31929755d4 (diff)
hp9845: re-factored handling of HTI images into a dedicated format module
Diffstat (limited to 'src/lib')
-rw-r--r--src/lib/formats/hti_tape.cpp400
-rw-r--r--src/lib/formats/hti_tape.h93
2 files changed, 493 insertions, 0 deletions
diff --git a/src/lib/formats/hti_tape.cpp b/src/lib/formats/hti_tape.cpp
new file mode 100644
index 00000000000..4b26f9778f8
--- /dev/null
+++ b/src/lib/formats/hti_tape.cpp
@@ -0,0 +1,400 @@
+// license:BSD-3-Clause
+// copyright-holders:F. Ulivi
+/*********************************************************************
+
+ "HTI" format
+
+*********************************************************************/
+
+#include "hti_tape.h"
+#include "imageutl.h"
+
+static constexpr uint32_t FILE_MAGIC = 0x5441434f; // Magic value at start of image file: "TACO"
+static constexpr hti_format_t::tape_pos_t ZERO_BIT_LEN = 619; // Length of 0 bits at slow tape speed: 1/(35200 Hz)
+static constexpr hti_format_t::tape_pos_t ONE_BIT_LEN = 1083; // Length of 1 bits at slow tape speed: 1.75 times ZERO_BIT_LEN
+
+// *** Position of tape holes ***
+// At beginning of tape:
+// *START*
+// |<-----24"----->|<---12"--->|<---12"--->|<-----24"----->|
+// O O O O O O O
+// |<->| |<->| |<->|
+// 0.218" 0.218" 0.218"
+// At end of tape:
+// *END*
+// |<-----24"----->|<---12"--->|<---12"--->|<-----24"----->|
+// O O O O
+//
+static const hti_format_t::tape_pos_t tape_holes[] = {
+ (hti_format_t::tape_pos_t)(23.891 * hti_format_t::ONE_INCH_POS), // 24 - 0.218 / 2
+ (hti_format_t::tape_pos_t)(24.109 * hti_format_t::ONE_INCH_POS), // 24 + 0.218 / 2
+ (hti_format_t::tape_pos_t)(35.891 * hti_format_t::ONE_INCH_POS), // 36 - 0.218 / 2
+ (hti_format_t::tape_pos_t)(36.109 * hti_format_t::ONE_INCH_POS), // 36 + 0.218 / 2
+ (hti_format_t::tape_pos_t)(47.891 * hti_format_t::ONE_INCH_POS), // 48 - 0.218 / 2
+ (hti_format_t::tape_pos_t)(48.109 * hti_format_t::ONE_INCH_POS), // 48 + 0.218 / 2
+ 72 * hti_format_t::ONE_INCH_POS, // 72
+ 1752 * hti_format_t::ONE_INCH_POS, // 1752
+ 1776 * hti_format_t::ONE_INCH_POS, // 1776
+ 1788 * hti_format_t::ONE_INCH_POS, // 1788
+ 1800 * hti_format_t::ONE_INCH_POS // 1800
+};
+
+hti_format_t::hti_format_t()
+{
+ clear_tape();
+}
+
+bool hti_format_t::load_tape(io_generic *io)
+{
+ uint8_t tmp[ 4 ];
+
+ io_generic_read(io, tmp, 0, 4);
+ if (pick_integer_be(tmp , 0 , 4) != FILE_MAGIC) {
+ return false;
+ }
+
+ uint64_t offset = 4;
+
+ for (tape_track_t& track : m_tracks) {
+ if (!load_track(io , offset , track)) {
+ clear_tape();
+ return false;
+ }
+ }
+
+ return true;
+}
+
+void hti_format_t::save_tape(io_generic *io)
+{
+ uint8_t tmp[ 4 ];
+
+ place_integer_be(tmp, 0, 4, FILE_MAGIC);
+ io_generic_write(io, tmp, 0, 4);
+
+ uint64_t offset = 4;
+
+ for (const tape_track_t& track : m_tracks) {
+ 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);
+ it_start = it;
+ n_words = 0;
+ }
+ next_pos = it->first + word_length(it->second);
+ n_words++;
+ }
+ dump_sequence(io , offset , 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;
+ }
+}
+
+void hti_format_t::clear_tape()
+{
+ for (tape_track_t& track : m_tracks) {
+ track.clear();
+ }
+}
+
+hti_format_t::tape_pos_t hti_format_t::word_length(tape_word_t w)
+{
+ unsigned zeros , ones;
+
+ // pop count of w
+ ones = (w & 0x5555) + ((w >> 1) & 0x5555);
+ ones = (ones & 0x3333) + ((ones >> 2) & 0x3333);
+ ones = (ones & 0x0f0f) + ((ones >> 4) & 0x0f0f);
+ ones = (ones & 0x00ff) + ((ones >> 8) & 0x00ff);
+
+ zeros = 16 - ones;
+
+ return zeros * ZERO_BIT_LEN + (ones + 1) * ONE_BIT_LEN;
+}
+
+hti_format_t::tape_pos_t hti_format_t::farthest_end(const track_iterator_t& it , bool forward)
+{
+ if (forward) {
+ return word_end_pos(it);
+ } else {
+ return it->first;
+ }
+}
+
+bool hti_format_t::pos_offset(tape_pos_t& pos , bool forward , tape_pos_t offset)
+{
+ if (offset == 0) {
+ return true;
+ }
+
+ if (!forward) {
+ offset = -offset;
+ }
+
+ pos += offset;
+
+ // In real life tape would unspool..
+ if (pos > TAPE_LENGTH) {
+ pos = TAPE_LENGTH;
+ return false;
+ } else if (pos < 0) {
+ pos = 0;
+ return false;
+ } else {
+ return true;
+ }
+}
+
+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) {
+ if (hole > pos) {
+ return hole;
+ }
+ }
+ // 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 ];
+ }
+ }
+ // No more holes: will hit start of tape
+ return NULL_TAPE_POS;
+ }
+}
+
+void hti_format_t::write_word(unsigned track_no , tape_pos_t start , tape_word_t word , tape_pos_t& length)
+{
+ tape_track_t& track = m_tracks[ track_no ];
+ track_iterator_t it_low = track.lower_bound(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.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)
+{
+ 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);
+ 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)
+{
+ 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);
+
+ adjust_it(track, it_low, a);
+
+ 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)
+{
+ tape_track_t& track = m_tracks[ track_no ];
+ it = track.lower_bound(pos);
+ if (forward) {
+ if (inclusive) {
+ adjust_it(track, it, pos);
+ }
+ return it != track.end();
+ } else {
+ // Never more than 2 iterations
+ do {
+ if (it == track.begin()) {
+ it = track.end();
+ return false;
+ }
+ --it;
+ } while (!inclusive && word_end_pos(it) > pos);
+ return true;
+ }
+}
+
+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 ];
+ if (forward) {
+ tape_pos_t prev_pos = word_end_pos(it);
+ ++it;
+ if (it == track.end()) {
+ return ADV_NO_MORE_DATA;
+ } else {
+ adv_res_t res = prev_pos == it->first ? ADV_CONT_DATA : ADV_DISCONT_DATA;
+ return res;
+ }
+ } else {
+ if (it == track.begin()) {
+ it = track.end();
+ return ADV_NO_MORE_DATA;
+ } else {
+ tape_pos_t prev_pos = it->first;
+ --it;
+ return prev_pos == word_end_pos(it) ? ADV_CONT_DATA : ADV_DISCONT_DATA;
+ }
+ }
+}
+
+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);
+ // Then scan for 1st gap
+ tape_track_t& track = m_tracks[ track_no ];
+ bool done = false;
+ track_iterator_t prev_it;
+ unsigned n_gaps = 1;
+
+ if (forward) {
+ tape_pos_t next_pos;
+
+ while (1) {
+ if (it == track.end()) {
+ next_pos = TAPE_LENGTH;
+ done = true;
+ } else {
+ next_pos = it->first;
+ }
+ if (((next_pos - pos) >= min_gap && --n_gaps == 0) || done) {
+ break;
+ }
+ adv_res_t adv_res;
+ do {
+ prev_it = it;
+ adv_res = adv_it(track_no , forward , it);
+ } while (adv_res == ADV_CONT_DATA);
+ pos = word_end_pos(prev_it);
+ }
+ } else {
+ tape_pos_t next_pos;
+
+ while (1) {
+ if (it == track.end()) {
+ next_pos = 0;
+ done = true;
+ } else {
+ next_pos = word_end_pos(it);
+ }
+ if (((pos - next_pos) >= min_gap && --n_gaps == 0) || done) {
+ break;
+ }
+ adv_res_t adv_res;
+ do {
+ prev_it = 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);
+
+ return n_gaps == 0;
+}
+
+bool hti_format_t::load_track(io_generic *io , uint64_t& offset , tape_track_t& track)
+{
+ uint8_t tmp[ 4 ];
+ uint32_t tmp32;
+
+ track.clear();
+
+ while (1) {
+ // Read no. of words to follow
+ io_generic_read(io, tmp, offset, 4);
+ offset += 4;
+
+ tmp32 = pick_integer_le(tmp, 0, 4);
+
+ // Track ends
+ if (tmp32 == (uint32_t)-1) {
+ return true;
+ }
+
+ 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);
+
+ tape_pos_t pos = (tape_pos_t)tmp32;
+
+ 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);
+
+ track.insert(std::make_pair(pos , tmp16));
+ pos += word_length(tmp16);
+ }
+ }
+}
+
+void hti_format_t::dump_sequence(io_generic *io , uint64_t& offset , 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;
+
+ 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;
+ ++it_start;
+ }
+ }
+}
+
+hti_format_t::tape_pos_t hti_format_t::word_end_pos(const track_iterator_t& it)
+{
+ 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)
+{
+ if (it != track.begin()) {
+ --it;
+ if (word_end_pos(it) <= pos) {
+ ++it;
+ }
+ }
+}
+
+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
+ tape_pos_t tmp;
+ tmp = a;
+ a = b;
+ b = tmp;
+ }
+}
diff --git a/src/lib/formats/hti_tape.h b/src/lib/formats/hti_tape.h
new file mode 100644
index 00000000000..cb8b1a39262
--- /dev/null
+++ b/src/lib/formats/hti_tape.h
@@ -0,0 +1,93 @@
+// license:BSD-3-Clause
+// copyright-holders:F. Ulivi
+/*********************************************************************
+
+ "HTI" format
+
+ Format of images of DC-100 tape cassettes as used in HP 9845
+ and HP 85 systems.
+
+*********************************************************************/
+
+#ifndef _HTI_TAPE_H_
+#define _HTI_TAPE_H_
+
+#pragma once
+
+#include <map>
+#include "ioprocs.h"
+
+class hti_format_t {
+public:
+ hti_format_t();
+
+ // Tape position, 1 unit = 1 inch / (968 * 1024)
+ typedef int32_t tape_pos_t;
+
+ // 1 inch in tape_pos_t
+ static constexpr tape_pos_t ONE_INCH_POS = 968 * 1024;
+
+ // Special value for invalid/unknown tape position
+ static constexpr tape_pos_t NULL_TAPE_POS = -1;
+
+ // Tape length: 140 ft of usable tape + 72" of punched tape at either end
+ static constexpr tape_pos_t TAPE_LENGTH = (140 * 12 + 72 * 2) * ONE_INCH_POS;
+
+ // Words stored on tape
+ typedef uint16_t tape_word_t;
+
+ // Storage of tracks: mapping from a tape position to word stored there
+ typedef std::map<tape_pos_t, tape_word_t> tape_track_t;
+
+ // Iterator to access words on tape
+ typedef tape_track_t::iterator track_iterator_t;
+
+ bool load_tape(io_generic *io);
+ void save_tape(io_generic *io);
+ void clear_tape();
+
+ // Return physical length of a 16-bit word on tape
+ static tape_pos_t word_length(tape_word_t w);
+
+ static tape_pos_t farthest_end(const track_iterator_t& it , bool forward);
+
+ static bool pos_offset(tape_pos_t& pos , bool forward , tape_pos_t offset);
+
+ // Position of next hole tape will reach in a given direction
+ static tape_pos_t next_hole(tape_pos_t pos , bool forward);
+
+ // Write a data word on tape
+ void write_word(unsigned track_no , tape_pos_t start , tape_word_t word , tape_pos_t& length);
+ // Write a gap on tape
+ void write_gap(unsigned track_no , tape_pos_t a , tape_pos_t b);
+
+ // Check that a section of tape has no data (it's just gap)
+ bool just_gap(unsigned track_no , tape_pos_t a , tape_pos_t b);
+
+ // Return position of next data word in a given direction
+ bool next_data(unsigned track_no , tape_pos_t pos , bool forward , bool inclusive , track_iterator_t& it);
+
+ typedef enum {
+ ADV_NO_MORE_DATA,
+ ADV_CONT_DATA,
+ ADV_DISCONT_DATA
+ } adv_res_t;
+
+ // Advance an iterator to next word of data
+ adv_res_t adv_it(unsigned track_no , bool forward , track_iterator_t& it);
+
+ // Scan for beginning of next gap in a given direction
+ bool next_gap(unsigned track_no , tape_pos_t& pos , bool forward , tape_pos_t min_gap);
+private:
+ // Content of tape tracks
+ tape_track_t m_tracks[ 2 ];
+
+ static bool load_track(io_generic *io , uint64_t& offset , tape_track_t& track);
+ static void dump_sequence(io_generic *io , uint64_t& offset , tape_track_t::const_iterator it_start , unsigned n_words);
+
+ static tape_pos_t word_end_pos(const track_iterator_t& it);
+ static void adjust_it(tape_track_t& track , track_iterator_t& it , tape_pos_t pos);
+ static void ensure_a_lt_b(tape_pos_t& a , tape_pos_t& b);
+};
+
+#endif /* _HTI_TAPE_H_ */