diff options
-rw-r--r-- | scripts/src/formats.lua | 12 | ||||
-rw-r--r-- | scripts/target/mame/mess.lua | 1 | ||||
-rw-r--r-- | src/devices/machine/hp_taco.cpp | 575 | ||||
-rw-r--r-- | src/devices/machine/hp_taco.h | 64 | ||||
-rw-r--r-- | src/lib/formats/hti_tape.cpp | 400 | ||||
-rw-r--r-- | src/lib/formats/hti_tape.h | 93 |
6 files changed, 610 insertions, 535 deletions
diff --git a/scripts/src/formats.lua b/scripts/src/formats.lua index b8bd7d3e38b..29a22b23ad0 100644 --- a/scripts/src/formats.lua +++ b/scripts/src/formats.lua @@ -907,6 +907,18 @@ end -------------------------------------------------- -- +--@src/lib/formats/hti_tape.h,FORMATS["HTI_TAP"] = true +-------------------------------------------------- + +if (FORMATS["HTI_TAP"]~=null or _OPTIONS["with-tools"]) then + files { + MAME_DIR.. "src/lib/formats/hti_tape.cpp", + MAME_DIR.. "src/lib/formats/hti_tape.h", + } +end + +-------------------------------------------------- +-- --@src/lib/formats/hpi_dsk.h,FORMATS["HPI_DSK"] = true -------------------------------------------------- diff --git a/scripts/target/mame/mess.lua b/scripts/target/mame/mess.lua index 329b0ab95e5..33a1cf081d6 100644 --- a/scripts/target/mame/mess.lua +++ b/scripts/target/mame/mess.lua @@ -814,6 +814,7 @@ FORMATS["GTP_CAS"] = true FORMATS["HECTOR_MINIDISC"] = true FORMATS["HECT_DSK"] = true FORMATS["HECT_TAP"] = true +FORMATS["HTI_TAP"] = true FORMATS["HPI_DSK"] = true FORMATS["HP_IPC_DSK"] = true FORMATS["IQ151_DSK"] = true diff --git a/src/devices/machine/hp_taco.cpp b/src/devices/machine/hp_taco.cpp index 359df5bd023..1a375a7accd 100644 --- a/src/devices/machine/hp_taco.cpp +++ b/src/devices/machine/hp_taco.cpp @@ -164,15 +164,11 @@ enum { #define STATUS_REG_MASK 0x003f // Status register mask #define TACH_TICKS_PER_INCH 968 // Tachometer pulses per inch of tape movement #define TAPE_POS_FRACT 1024 // 10 bits of fractional part in tape_pos_t -#define ONE_INCH_POS (TACH_TICKS_PER_INCH * TAPE_POS_FRACT) // Value in tape_pos_t representing 1 inch of tape #define TACH_FREQ_SLOW 21276 // Tachometer pulse frequency for slow speed (21.98 ips) #define TACH_FREQ_FAST 87196 // Tachometer pulse frequency for fast speed (90.08 ips) #define TACH_FREQ_BRAKE_SLOW 11606 // Tachometer pulse frequency when stopping from slow speed (11.99 ips) #define TACH_FREQ_BRAKE_FAST 44566 // Tachometer pulse frequency when stopping from fast speed (46.04 ips) -#define TAPE_LENGTH ((140 * 12 + 72 * 2) * ONE_INCH_POS) // Tape length: 140 ft of usable tape + 72" of punched tape at either end -#define TAPE_INIT_POS (80 * ONE_INCH_POS) // Initial tape position: 80" from beginning (just past the punched part) -#define ZERO_BIT_LEN 619 // Length of 0 bits at slow tape speed: 1/(35200 Hz) -#define ONE_BIT_LEN 1083 // Length of 1 bits at slow tape speed: 1.75 times ZERO_BIT_LEN +#define TAPE_INIT_POS (80 * hti_format_t::ONE_INCH_POS) // Initial tape position: 80" from beginning (just past the punched part) #define QUICK_CMD_USEC 25 // usec for "quick" command execution (totally made up) #define FAST_BRAKE_DIST 3350450 // Braking distance at fast speed (~3.38 in) // There are 2 braking distances here: The first one (commented out) is the theoretical value, the second one @@ -182,15 +178,13 @@ enum { //#define SLOW_BRAKE_DIST 197883 // Braking distance at slow speed (~0.2 in) #define SLOW_BRAKE_DIST 71000 // Braking distance at slow speed (~0.07 in) #define PREAMBLE_WORD 0 // Value of preamble word -#define END_GAP_LENGTH (6 * ONE_INCH_POS) // Length of final gap: 6" +#define END_GAP_LENGTH (6 * hti_format_t::ONE_INCH_POS) // Length of final gap: 6" // Minimum gap lengths are probably counted from tacho pulses in real TACO: short gaps could be equal to 64 pulses and long ones // to 1472 (23 * 64) -#define SHORT_GAP_LENGTH ((tape_pos_t)(0.066 * ONE_INCH_POS)) // Minimum length of short gaps: 0.066" ([1], pg 8-10) -#define LONG_GAP_LENGTH ((tape_pos_t)(1.5 * ONE_INCH_POS)) // Minimum length of long gaps: 1.5" ([1], pg 8-10) -#define NULL_TAPE_POS ((tape_pos_t)-1) // Special value for invalid/unknown tape position -#define PREAMBLE_TIMEOUT ((tape_pos_t)(2.6 * ONE_INCH_POS)) // Min. length of gap making preamble search time out (totally made up) -#define DATA_TIMEOUT ((tape_pos_t)(0.066 * ONE_INCH_POS)) // Min. length of gap that will cause data reading to time out (totally made up) -#define FILE_MAGIC 0x4f434154 // Magic value at start of image file: "TACO" +#define SHORT_GAP_LENGTH ((hti_format_t::tape_pos_t)(0.066 * hti_format_t::ONE_INCH_POS)) // Minimum length of short gaps: 0.066" ([1], pg 8-10) +#define LONG_GAP_LENGTH ((hti_format_t::tape_pos_t)(1.5 * hti_format_t::ONE_INCH_POS)) // Minimum length of long gaps: 1.5" ([1], pg 8-10) +#define PREAMBLE_TIMEOUT ((hti_format_t::tape_pos_t)(2.6 * hti_format_t::ONE_INCH_POS)) // Min. length of gap making preamble search time out (totally made up) +#define DATA_TIMEOUT ((hti_format_t::tape_pos_t)(0.066 * hti_format_t::ONE_INCH_POS)) // Min. length of gap that will cause data reading to time out (totally made up) // Parts of command register #define CMD_CODE(reg) \ @@ -255,32 +249,6 @@ enum { #define STATUS_WPR_MASK BIT_MASK(STATUS_WPR_BIT) // Write protection #define STATUS_ERR_MASK (STATUS_CART_OUT_MASK) // Mask of errors in status reg. -// *** 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 -// -const hp_taco_device::tape_pos_t hp_taco_device::tape_holes[] = { - (hp_taco_device::tape_pos_t)(23.891 * ONE_INCH_POS), // 24 - 0.218 / 2 - (hp_taco_device::tape_pos_t)(24.109 * ONE_INCH_POS), // 24 + 0.218 / 2 - (hp_taco_device::tape_pos_t)(35.891 * ONE_INCH_POS), // 36 - 0.218 / 2 - (hp_taco_device::tape_pos_t)(36.109 * ONE_INCH_POS), // 36 + 0.218 / 2 - (hp_taco_device::tape_pos_t)(47.891 * ONE_INCH_POS), // 48 - 0.218 / 2 - (hp_taco_device::tape_pos_t)(48.109 * ONE_INCH_POS), // 48 + 0.218 / 2 - 72 * ONE_INCH_POS, // 72 - 1752 * ONE_INCH_POS, // 1752 - 1776 * ONE_INCH_POS, // 1776 - 1788 * ONE_INCH_POS, // 1788 - 1800 * ONE_INCH_POS // 1800 -}; - // Device type definition DEFINE_DEVICE_TYPE(HP_TACO, hp_taco_device, "hp_taco", "HP TACO") @@ -291,6 +259,7 @@ hp_taco_device::hp_taco_device(const machine_config &mconfig, device_type type, m_irq_handler(*this), m_flg_handler(*this), m_sts_handler(*this), + m_image(), m_image_dirty(false) { clear_state(); @@ -460,7 +429,7 @@ void hp_taco_device::device_timer(emu_timer &timer, device_timer_id id, int para switch (CMD_CODE(m_cmd_reg)) { case CMD_FINAL_GAP: case CMD_WRITE_IRG: - write_gap(m_rw_pos , m_tape_pos); + m_image.write_gap(current_track() , m_rw_pos , m_tape_pos); m_rw_pos = m_tape_pos; break; @@ -547,7 +516,7 @@ void hp_taco_device::clear_state(void) m_rw_pos = 0; m_next_word = 0; m_rd_it_valid = false; - m_gap_detect_start = NULL_TAPE_POS; + m_gap_detect_start = hti_format_t::NULL_TAPE_POS; set_tape_present(false); set_tape_present(is_loaded()); @@ -581,31 +550,7 @@ unsigned hp_taco_device::speed_to_tick_freq(void) const (m_tape_fast ? TACH_FREQ_FAST * TAPE_POS_FRACT : TACH_FREQ_SLOW * TAPE_POS_FRACT); } -bool hp_taco_device::pos_offset(tape_pos_t& pos , tape_pos_t offset) const -{ - if (offset == 0) { - return true; - } - - if (!m_tape_fwd) { - 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; - } -} - -hp_taco_device::tape_pos_t hp_taco_device::current_tape_pos(void) const +hti_format_t::tape_pos_t hp_taco_device::current_tape_pos(void) const { if (m_start_time.is_never()) { // Tape not moving @@ -615,11 +560,11 @@ hp_taco_device::tape_pos_t hp_taco_device::current_tape_pos(void) const attotime delta_time(machine().time() - m_start_time); LOG_0(("delta_time = %g\n" , delta_time.as_double())); // How many tachometer ticks has the tape moved? - tape_pos_t delta_tach = (tape_pos_t)(delta_time.as_ticks(speed_to_tick_freq())); + hti_format_t::tape_pos_t delta_tach = (hti_format_t::tape_pos_t)(delta_time.as_ticks(speed_to_tick_freq())); LOG_0(("delta_tach = %u\n" , delta_tach)); - tape_pos_t tape_pos = m_tape_pos; - if (!pos_offset(tape_pos , delta_tach)) { + auto tape_pos = m_tape_pos; + if (!hti_format_t::pos_offset(tape_pos , m_tape_fwd , delta_tach)) { LOG(("Tape unspooled!\n")); } @@ -639,10 +584,10 @@ void hp_taco_device::update_tape_pos(void) // Gap detection bool gap_detected = false; - if (m_gap_detect_start != NULL_TAPE_POS && abs(m_gap_detect_start - m_tape_pos) >= min_gap_size()) { - tape_pos_t tmp = m_tape_pos; - pos_offset(tmp , -min_gap_size()); - gap_detected = just_gap(tmp , m_tape_pos); + if (m_gap_detect_start != hti_format_t::NULL_TAPE_POS && abs(m_gap_detect_start - m_tape_pos) >= min_gap_size()) { + auto tmp = m_tape_pos; + hti_format_t::pos_offset(tmp , m_tape_fwd , -min_gap_size()); + gap_detected = m_image.just_gap(current_track() , tmp , m_tape_pos); } if (gap_detected) { BIT_SET(m_status_reg, STATUS_GAP_BIT); @@ -660,9 +605,9 @@ void hp_taco_device::update_tach_reg(void) return; } - tape_pos_t pos = current_tape_pos(); - tape_pos_t pos_int = pos / TAPE_POS_FRACT; - tape_pos_t ref_int = m_tach_reg_ref / TAPE_POS_FRACT; + auto pos = current_tape_pos(); + hti_format_t::tape_pos_t pos_int = pos / TAPE_POS_FRACT; + hti_format_t::tape_pos_t ref_int = m_tach_reg_ref / TAPE_POS_FRACT; uint16_t reg_value = (uint16_t)(abs(pos_int - ref_int) + m_tach_reg); LOG_0(("Tach = %04x @ pos = %d, ref_pos = %d\n" , reg_value , pos , m_tach_reg_ref)); @@ -682,48 +627,13 @@ void hp_taco_device::freeze_tach_reg(bool freeze) } -void hp_taco_device::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; - } -} - -// Position of next hole tape will reach in a given direction -hp_taco_device::tape_pos_t hp_taco_device::next_hole(void) const -{ - if (m_tape_fwd) { - for (tape_pos_t hole : tape_holes) { - if (hole > m_tape_pos) { - LOG_0(("next hole fwd @%u = %u\n" , m_tape_pos , hole)); - 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 ] < m_tape_pos) { - LOG_0(("next hole rev @%u = %u\n" , m_tape_pos , tape_holes[ i ])); - return tape_holes[ i ]; - } - } - // No more holes: will hit start of tape - return NULL_TAPE_POS; - } -} - -attotime hp_taco_device::time_to_distance(tape_pos_t distance) const +attotime hp_taco_device::time_to_distance(hti_format_t::tape_pos_t distance) const { // +1 for rounding return attotime::from_ticks(distance + 1 , speed_to_tick_freq()); } -attotime hp_taco_device::time_to_target(tape_pos_t target) const +attotime hp_taco_device::time_to_target(hti_format_t::tape_pos_t target) const { return time_to_distance(abs(target - m_tape_pos)); } @@ -758,7 +668,7 @@ bool hp_taco_device::start_tape_cmd(uint16_t cmd_reg , uint16_t must_be_1 , uint if (m_tape_wr) { // Write command: disable gap detector - m_gap_detect_start = NULL_TAPE_POS; + m_gap_detect_start = hti_format_t::NULL_TAPE_POS; BIT_CLR(m_status_reg, STATUS_GAP_BIT); m_image_dirty = true; } else if (not_moving || prev_tape_braking || prev_tape_wr != m_tape_wr || prev_tape_fwd != m_tape_fwd || prev_tape_fast != m_tape_fast) { @@ -791,140 +701,12 @@ bool hp_taco_device::start_tape_cmd(uint16_t cmd_reg , uint16_t must_be_1 , uint void hp_taco_device::stop_tape(void) { m_start_time = attotime::never; - m_gap_detect_start = NULL_TAPE_POS; -} - -hp_taco_device::tape_track_t& hp_taco_device::current_track(void) -{ - return m_tracks[ BIT(m_status_reg , STATUS_TRACKB_BIT) ]; -} - -// Return physical length of a 16-bit word on tape -hp_taco_device::tape_pos_t hp_taco_device::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; + m_gap_detect_start = hti_format_t::NULL_TAPE_POS; } -hp_taco_device::tape_pos_t hp_taco_device::word_end_pos(const tape_track_t::iterator& it) +unsigned hp_taco_device::current_track(void) { - return it->first + word_length(it->second); -} - -void hp_taco_device::adjust_it(tape_track_t& track , tape_track_t::iterator& it , tape_pos_t pos) -{ - if (it != track.begin()) { - --it; - if (word_end_pos(it) <= pos) { - ++it; - } - } -} - -// Write a word on current tape track -void hp_taco_device::write_word(tape_pos_t start , tape_word_t word , tape_pos_t& length) -{ - tape_track_t& track = current_track(); - tape_track_t::iterator it_low = track.lower_bound(start); - adjust_it(track , it_low , start); - length = word_length(word); - tape_pos_t end_pos = start + length; - tape_track_t::iterator it_high = track.lower_bound(end_pos); - - track.erase(it_low , it_high); - - track.insert(it_high , std::make_pair(start, word)); - LOG_0(("WR %04x @ T%u:%u\n" , word , BIT(m_status_reg , STATUS_TRACKB_BIT) , start)); -} - -// Write a gap on current track -void hp_taco_device::write_gap(tape_pos_t a , tape_pos_t b) -{ - ensure_a_lt_b(a , b); - tape_track_t& track = current_track(); - tape_track_t::iterator it_low = track.lower_bound(a); - adjust_it(track , it_low , a); - tape_track_t::iterator it_high = track.lower_bound(b); - - track.erase(it_low, it_high); - - LOG_0(("GAP on T%u:[%u,%u)\n" , BIT(m_status_reg , STATUS_TRACKB_BIT) , a , b)); -} - -bool hp_taco_device::just_gap(tape_pos_t a , tape_pos_t b) -{ - ensure_a_lt_b(a , b); - tape_track_t& track = current_track(); - tape_track_t::iterator it_low = track.lower_bound(a); - tape_track_t::iterator it_high = track.lower_bound(b); - - adjust_it(track, it_low, a); - - return it_low == it_high; -} - -hp_taco_device::tape_pos_t hp_taco_device::farthest_end(const tape_track_t::iterator& it) const -{ - if (m_tape_fwd) { - return word_end_pos(it); - } else { - return it->first; - } -} - -bool hp_taco_device::next_data(tape_track_t::iterator& it , tape_pos_t pos , bool inclusive) -{ - tape_track_t& track = current_track(); - it = track.lower_bound(pos); - if (m_tape_fwd) { - 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; - } -} - -hp_taco_device::adv_res_t hp_taco_device::adv_it(tape_track_t::iterator& it) -{ - tape_track_t& track = current_track(); - if (m_tape_fwd) { - 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; - } - } + return BIT(m_status_reg , STATUS_TRACKB_BIT); } attotime hp_taco_device::fetch_next_wr_word(void) @@ -945,209 +727,24 @@ attotime hp_taco_device::fetch_next_wr_word(void) // Update checksum with new word m_checksum_reg += m_next_word; - return time_to_distance(word_length(m_next_word)); + return time_to_distance(hti_format_t::word_length(m_next_word)); } -attotime hp_taco_device::time_to_rd_next_word(tape_pos_t& word_rd_pos) +attotime hp_taco_device::time_to_rd_next_word(hti_format_t::tape_pos_t& word_rd_pos) { if (m_rd_it_valid) { - word_rd_pos = farthest_end(m_rd_it); + word_rd_pos = hti_format_t::farthest_end(m_rd_it , m_tape_fwd); return time_to_target(word_rd_pos); } else { return attotime::never; } } -hp_taco_device::tape_pos_t hp_taco_device::min_gap_size(void) const +hti_format_t::tape_pos_t hp_taco_device::min_gap_size(void) const { return LONG_GAP(m_cmd_reg) ? LONG_GAP_LENGTH : SHORT_GAP_LENGTH; } -/** - * Scan for next "n_gaps" gaps - * - * @param[in,out] pos Start position on input, start of gap on output - * @param it Pointer to data word where scan is to start - * @param n_gaps Number of gaps to scan - * @param min_gap Minimum gap size - * - * @return true if n_gaps gaps are found - */ -bool hp_taco_device::next_n_gap(tape_pos_t& pos , tape_track_t::iterator it , unsigned n_gaps , tape_pos_t min_gap) -{ - tape_track_t& track = current_track(); - bool done = false; - tape_track_t::iterator prev_it; - - if (m_tape_fwd) { - 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(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(it); - } while (adv_res == ADV_CONT_DATA); - pos = prev_it->first; - } - } - - // Set "pos" where minimum gap size is met - pos_offset(pos , min_gap); - - return n_gaps == 0; -} - -bool hp_taco_device::next_n_gap(tape_pos_t& pos , unsigned n_gaps , tape_pos_t min_gap) -{ - tape_track_t::iterator it; - // First align with next data - next_data(it, pos, true); - // Then scan for n_gaps - return next_n_gap(pos, it, n_gaps, min_gap); -} - -void hp_taco_device::clear_tape(void) -{ - for (tape_track_t& track : m_tracks) { - track.clear(); - } -} - -void hp_taco_device::dump_sequence(tape_track_t::const_iterator it_start , unsigned n_words) -{ - if (n_words) { - uint32_t tmp32; - uint16_t tmp16; - - tmp32 = n_words; - fwrite(&tmp32 , sizeof(tmp32)); - tmp32 = it_start->first; - fwrite(&tmp32 , sizeof(tmp32)); - - for (unsigned i = 0; i < n_words; i++) { - tmp16 = it_start->second; - fwrite(&tmp16 , sizeof(tmp16)); - ++it_start; - } - } -} - -void hp_taco_device::save_tape(void) -{ - uint32_t tmp32; - - fseek(0, SEEK_SET); - - tmp32 = FILE_MAGIC; - fwrite(&tmp32 , sizeof(tmp32)); - - 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(it_start , n_words); - it_start = it; - n_words = 0; - } - next_pos = it->first + word_length(it->second); - n_words++; - } - dump_sequence(it_start , n_words); - // End of track - tmp32 = (uint32_t)-1; - fwrite(&tmp32 , sizeof(tmp32)); - } -} - -bool hp_taco_device::load_track(tape_track_t& track) -{ - uint32_t tmp32; - - track.clear(); - - while (1) { - if (fread(&tmp32 , sizeof(tmp32)) != sizeof(tmp32)) { - return false; - } - - if (tmp32 == (uint32_t)-1) { - return true; - } - - unsigned n_words = tmp32; - - if (fread(&tmp32 , sizeof(tmp32)) != sizeof(tmp32)) { - return false; - } - - tape_pos_t pos = (tape_pos_t)tmp32; - - for (unsigned i = 0; i < n_words; i++) { - uint16_t tmp16; - - if (fread(&tmp16 , sizeof(tmp16)) != sizeof(tmp16)) { - return false; - } - - track.insert(std::make_pair(pos , tmp16)); - pos += word_length(tmp16); - } - } -} - -bool hp_taco_device::load_tape(void) -{ - uint32_t magic; - - if (fread(&magic , sizeof(magic)) != sizeof(magic) || - magic != FILE_MAGIC) { - return false; - } - - for (tape_track_t& track : m_tracks) { - if (!load_track(track)) { - LOG(("load_tape failed")); - clear_tape(); - return false; - } - } - - LOG(("load_tape done\n")); - return true; -} - void hp_taco_device::set_tape_present(bool present) { if (present) { @@ -1165,9 +762,9 @@ void hp_taco_device::set_tape_present(bool present) attotime hp_taco_device::time_to_next_hole(void) const { - tape_pos_t pos = next_hole(); + auto pos = hti_format_t::next_hole(m_tape_pos , m_tape_fwd); - if (pos == NULL_TAPE_POS) { + if (pos == hti_format_t::NULL_TAPE_POS) { return attotime::never; } else { return time_to_target(pos); @@ -1176,7 +773,7 @@ attotime hp_taco_device::time_to_next_hole(void) const attotime hp_taco_device::time_to_tach_pulses(void) const { - return time_to_distance((tape_pos_t)(0x10000U - m_tach_reg) * TAPE_POS_FRACT); + return time_to_distance((hti_format_t::tape_pos_t)(0x10000U - m_tach_reg) * TAPE_POS_FRACT); } void hp_taco_device::terminate_cmd_now(void) @@ -1236,7 +833,7 @@ void hp_taco_device::cmd_fsm(void) m_cmd_state = CMD_PH1; } else { // PH1 - write_gap(m_rw_pos , m_tape_pos); + m_image.write_gap(current_track() , m_rw_pos , m_tape_pos); cmd_duration = attotime::zero; m_cmd_state = CMD_END; } @@ -1246,7 +843,7 @@ void hp_taco_device::cmd_fsm(void) if (m_cmd_state == CMD_PH0) { // PH0 // Search for preamble first - m_rd_it_valid = next_data(m_rd_it , m_tape_pos , false); + m_rd_it_valid = m_image.next_data(current_track() , m_tape_pos , m_tape_fwd , false , m_rd_it); cmd_duration = time_to_rd_next_word(m_rw_pos); // Set T/O for preamble search set_data_timeout(true); @@ -1262,8 +859,8 @@ void hp_taco_device::cmd_fsm(void) m_timeout_timer->reset(); irq_w(true); } else { - adv_res_t res = adv_it(m_rd_it); - if (res != ADV_NO_MORE_DATA) { + auto res = m_image.adv_it(current_track() , m_tape_fwd , m_rd_it); + if (res != hti_format_t::ADV_NO_MORE_DATA) { cmd_duration = time_to_rd_next_word(m_rw_pos); } // Set T/O for arrival of data words @@ -1278,13 +875,13 @@ void hp_taco_device::cmd_fsm(void) // PH0 m_next_word = PREAMBLE_WORD; m_rw_pos = m_tape_pos; - cmd_duration = time_to_distance(word_length(m_next_word)); + cmd_duration = time_to_distance(hti_format_t::word_length(m_next_word)); m_cmd_state = CMD_PH1; } else { // PH1 & PH2 of CMD_RECORD_WRITE - tape_pos_t length; - write_word(m_rw_pos , m_next_word , length); - pos_offset(m_rw_pos , length); + hti_format_t::tape_pos_t length; + m_image.write_word(current_track() , m_rw_pos , m_next_word , length); + hti_format_t::pos_offset(m_rw_pos , m_tape_fwd , length); // Just to be sure.. m_tape_pos = m_rw_pos; cmd_duration = fetch_next_wr_word(); @@ -1317,8 +914,8 @@ void hp_taco_device::cmd_fsm(void) case CMD_INGAP_MOVE: if (m_cmd_state == CMD_PH0) { // PH0 - tape_pos_t target = m_tape_pos; - if (next_n_gap(target, 1, min_gap_size())) { + auto target = m_tape_pos; + if (m_image.next_gap(current_track() , target, m_tape_fwd, min_gap_size())) { LOG_0(("IRG @%d\n" , target)); cmd_duration = time_to_target(target); } @@ -1335,16 +932,16 @@ void hp_taco_device::cmd_fsm(void) case CMD_NOT_INDTA: if (m_cmd_state == CMD_PH0) { // PH0 - if (next_data(m_rd_it , m_tape_pos , true)) { - cmd_duration = time_to_target(farthest_end(m_rd_it)); + if (m_image.next_data(current_track() , m_tape_pos , m_tape_fwd , true , m_rd_it)) { + cmd_duration = time_to_target(hti_format_t::farthest_end(m_rd_it , m_tape_fwd)); } // Set T/O for data set_data_timeout(true); m_cmd_state = CMD_PH1; } else { // PH1 - tape_pos_t target = m_tape_pos; - if (next_n_gap(target, 1, min_gap_size())) { + auto target = m_tape_pos; + if (m_image.next_gap(current_track() , target, m_tape_fwd, min_gap_size())) { LOG_0(("End of data @%d\n" , target)); cmd_duration = time_to_target(target); } @@ -1363,7 +960,7 @@ void hp_taco_device::cmd_fsm(void) m_cmd_state = CMD_PH1; } else { // PH1 - write_gap(m_rw_pos , m_tape_pos); + m_image.write_gap(current_track() , m_rw_pos , m_tape_pos); cmd_duration = attotime::zero; m_cmd_state = CMD_END; } @@ -1381,16 +978,16 @@ void hp_taco_device::cmd_fsm(void) break; } } - if (next_data(m_rd_it , m_tape_pos , true)) { - cmd_duration = time_to_target(farthest_end(m_rd_it)); + if (m_image.next_data(current_track() , m_tape_pos , m_tape_fwd , true , m_rd_it)) { + cmd_duration = time_to_target(hti_format_t::farthest_end(m_rd_it , m_tape_fwd)); } // Set T/O for data set_data_timeout(true); m_cmd_state = CMD_PH1; } else if (m_cmd_state == CMD_PH1) { // PH1 - tape_pos_t target = m_tape_pos; - if (next_n_gap(target, 1, min_gap_size())) { + auto target = m_tape_pos; + if (m_image.next_gap(current_track() , target, m_tape_fwd, min_gap_size())) { LOG_0(("Gap @%d (%u to go)\n" , target , 0x10000U - m_tach_reg)); cmd_duration = time_to_target(target); } @@ -1407,8 +1004,8 @@ void hp_taco_device::cmd_fsm(void) m_cmd_state = CMD_PH1; } else { // PH1 - if (next_data(m_rd_it , m_tape_pos , true)) { - cmd_duration = time_to_target(farthest_end(m_rd_it)); + if (m_image.next_data(current_track() , m_tape_pos , m_tape_fwd , true , m_rd_it)) { + cmd_duration = time_to_target(hti_format_t::farthest_end(m_rd_it , m_tape_fwd)); } // Apparently this cmd doesn't set no-data T/O m_cmd_state = CMD_END; @@ -1430,8 +1027,8 @@ void hp_taco_device::cmd_fsm(void) m_cmd_state = CMD_PH1; } else { // PH1 - tape_pos_t target = m_tape_pos; - if (next_n_gap(target, 1, min_gap_size())) { + auto target = m_tape_pos; + if (m_image.next_gap(current_track() , target, m_tape_fwd, min_gap_size())) { LOG_0(("GAP @%d\n" , target)); cmd_duration = time_to_target(target); } @@ -1445,7 +1042,7 @@ void hp_taco_device::cmd_fsm(void) // Should also check if tape position has gone too far to read word @ m_rd_it if (!m_rd_it_valid) { // Search for preamble first - m_rd_it_valid = next_data(m_rd_it , m_tape_pos , false); + m_rd_it_valid = m_image.next_data(current_track() , m_tape_pos , m_tape_fwd , false , m_rd_it); // Set T/O for preamble search set_data_timeout(true); @@ -1483,9 +1080,9 @@ void hp_taco_device::cmd_fsm(void) } // Set T/O for arrival of data words set_data_timeout(false); - adv_res_t res = adv_it(m_rd_it); + auto res = m_image.adv_it(current_track() , m_tape_fwd , m_rd_it); LOG_0(("adv_it %d\n" , res)); - if (res == ADV_NO_MORE_DATA) { + if (res == hti_format_t::ADV_NO_MORE_DATA) { m_rd_it_valid = false; } } @@ -1511,8 +1108,8 @@ void hp_taco_device::cmd_fsm(void) // Note: checksum is not updated m_data_reg = m_rd_it->second; LOG_0(("Final RD %04x\n" , m_data_reg)); - adv_res_t res = adv_it(m_rd_it); - if (res == ADV_NO_MORE_DATA) { + auto res = m_image.adv_it(current_track() , m_tape_fwd , m_rd_it); + if (res == hti_format_t::ADV_NO_MORE_DATA) { m_rd_it_valid = false; } cmd_duration = attotime::zero; @@ -1672,20 +1269,24 @@ void hp_taco_device::start_cmd_exec(uint16_t new_cmd_reg) image_init_result hp_taco_device::internal_load(bool is_create) { - device_reset(); - - if (is_create) { - clear_tape(); - save_tape(); - } else if (!load_tape()) { - seterror(IMAGE_ERROR_INVALIDIMAGE , "Wrong format"); - set_tape_present(false); - return image_init_result::FAIL; - } + device_reset(); + + io_generic io; + io.file = (device_image_interface *)this; + io.procs = &image_ioprocs; + io.filler = 0; + if (is_create) { + m_image.clear_tape(); + m_image.save_tape(&io); + } else if (!m_image.load_tape(&io)) { + seterror(IMAGE_ERROR_INVALIDIMAGE , "Wrong format"); + set_tape_present(false); + return image_init_result::FAIL; + } - m_image_dirty = false; + m_image_dirty = false; - set_tape_present(true); + set_tape_present(true); return image_init_result::PASS; } @@ -1703,17 +1304,21 @@ image_init_result hp_taco_device::call_create(int format_type, util::option_reso void hp_taco_device::call_unload() { - LOG(("call_unload dirty=%d\n" , m_image_dirty)); + LOG(("call_unload dirty=%d\n" , m_image_dirty)); - device_reset(); + device_reset(); - if (m_image_dirty) { - save_tape(); - m_image_dirty = false; - } + if (m_image_dirty) { + io_generic io; + io.file = (device_image_interface *)this; + io.procs = &image_ioprocs; + io.filler = 0; + m_image.save_tape(&io); + m_image_dirty = false; + } - clear_tape(); - set_tape_present(false); + m_image.clear_tape(); + set_tape_present(false); } std::string hp_taco_device::call_display() @@ -1739,7 +1344,7 @@ std::string hp_taco_device::call_display() m2 = m_tape_fast ? '<' : ' '; } - int pos_in = current_tape_pos() / ONE_INCH_POS; + int pos_in = current_tape_pos() / hti_format_t::ONE_INCH_POS; buffer = string_format("%c %c %c%c [%04d/1824]" , track , r_w , m1 , m2 , pos_in); diff --git a/src/devices/machine/hp_taco.h b/src/devices/machine/hp_taco.h index c0536eabb9c..d5fe4fb9b23 100644 --- a/src/devices/machine/hp_taco.h +++ b/src/devices/machine/hp_taco.h @@ -13,7 +13,7 @@ #pragma once -#include <map> +#include "formats/hti_tape.h" #define MCFG_TACO_IRQ_HANDLER(_devcb) \ devcb = &hp_taco_device::set_irq_handler(*device , DEVCB_##_devcb); @@ -58,14 +58,6 @@ public: virtual const char *file_extensions() const override; protected: - // Tape position, 1 unit = 1 inch / (968 * 1024) - typedef int32_t tape_pos_t; - - // Words stored on tape - typedef uint16_t tape_word_t; - - static const tape_pos_t tape_holes[]; - hp_taco_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock); // device-level overrides @@ -75,9 +67,6 @@ protected: virtual void device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr) override; private: - // Storage of tracks: mapping from a tape position to word stored there - typedef std::map<tape_pos_t, tape_word_t> tape_track_t; - devcb_write_line m_irq_handler; devcb_write_line m_flg_handler; devcb_write_line m_sts_handler; @@ -88,7 +77,7 @@ private: uint16_t m_cmd_reg; uint16_t m_status_reg; uint16_t m_tach_reg; - tape_pos_t m_tach_reg_ref; + hti_format_t::tape_pos_t m_tach_reg_ref; bool m_tach_reg_frozen; uint16_t m_checksum_reg; bool m_clear_checksum_reg; @@ -112,7 +101,7 @@ private: cmd_state_t m_cmd_state; // Tape position & motion - tape_pos_t m_tape_pos; + hti_format_t::tape_pos_t m_tape_pos; attotime m_start_time; // Tape moving if != never bool m_tape_fwd; bool m_tape_fast; @@ -122,63 +111,38 @@ private: emu_timer *m_hole_timer; emu_timer *m_timeout_timer; - // Content of tape tracks - tape_track_t m_tracks[ 2 ]; + // Content of tape + hti_format_t m_image; bool m_image_dirty; // Reading & writing bool m_tape_wr; - tape_pos_t m_rw_pos; + hti_format_t::tape_pos_t m_rw_pos; uint16_t m_next_word; - tape_track_t::iterator m_rd_it; + hti_format_t::track_iterator_t m_rd_it; bool m_rd_it_valid; // Gap detection - tape_pos_t m_gap_detect_start; - - typedef enum { - ADV_NO_MORE_DATA, - ADV_CONT_DATA, - ADV_DISCONT_DATA - } adv_res_t; + hti_format_t::tape_pos_t m_gap_detect_start; void clear_state(void); void irq_w(bool state); void set_error(bool state); bool is_braking(void) const; unsigned speed_to_tick_freq(void) const; - bool pos_offset(tape_pos_t& pos , tape_pos_t offset) const; - tape_pos_t current_tape_pos(void) const; + hti_format_t::tape_pos_t current_tape_pos(void) const; void update_tape_pos(void); void update_tach_reg(void); void freeze_tach_reg(bool freeze); - static void ensure_a_lt_b(tape_pos_t& a , tape_pos_t& b); - tape_pos_t next_hole(void) const; - attotime time_to_distance(tape_pos_t distance) const; - attotime time_to_target(tape_pos_t target) const; + attotime time_to_distance(hti_format_t::tape_pos_t distance) const; + attotime time_to_target(hti_format_t::tape_pos_t target) const; attotime time_to_stopping_pos(void) const; bool start_tape_cmd(uint16_t cmd_reg , uint16_t must_be_1 , uint16_t must_be_0); void stop_tape(void); - tape_track_t& current_track(void); - static tape_pos_t word_length(tape_word_t w); - static tape_pos_t word_end_pos(const tape_track_t::iterator& it); - static void adjust_it(tape_track_t& track , tape_track_t::iterator& it , tape_pos_t pos); - void write_word(tape_pos_t start , tape_word_t word , tape_pos_t& length); - void write_gap(tape_pos_t a , tape_pos_t b); - bool just_gap(tape_pos_t a , tape_pos_t b); - tape_pos_t farthest_end(const tape_track_t::iterator& it) const; - bool next_data(tape_track_t::iterator& it , tape_pos_t pos , bool inclusive); - adv_res_t adv_it(tape_track_t::iterator& it); + unsigned current_track(void); attotime fetch_next_wr_word(void); - attotime time_to_rd_next_word(tape_pos_t& word_rd_pos); - tape_pos_t min_gap_size(void) const; - bool next_n_gap(tape_pos_t& pos , tape_track_t::iterator it , unsigned n_gaps , tape_pos_t min_gap); - bool next_n_gap(tape_pos_t& pos , unsigned n_gaps , tape_pos_t min_gap); - void clear_tape(void); - void dump_sequence(tape_track_t::const_iterator it_start , unsigned n_words); - void save_tape(void); - bool load_track(tape_track_t& track); - bool load_tape(void); + attotime time_to_rd_next_word(hti_format_t::tape_pos_t& word_rd_pos); + hti_format_t::tape_pos_t min_gap_size(void) const; void set_tape_present(bool present); attotime time_to_next_hole(void) const; attotime time_to_tach_pulses(void) const; 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_ */ |