summaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
author fulivi <fulivi@users.noreply.github.com>2021-01-12 13:29:30 +0100
committer GitHub <noreply@github.com>2021-01-12 23:29:30 +1100
commitfad7c9e0beb50f6c96da9ea8f93082bc349a25c2 (patch)
tree172a75715375b6627303798660004fd80dd2a75d
parentba587a777c41837c8aa9f2a2e8c1fc27b4616623 (diff)
hp2640.cpp: Added tape emulation. (#7625)
* formats/hti_tape.cpp: Added support for Manchester encoded DC100 cassettes. * machine/hp2640_tape.cpp: added emulation of DC100 tape drives. * machine/hp_dc100_tape.cpp: Added unit name display.
-rw-r--r--scripts/target/mame/mess.lua2
-rw-r--r--src/devices/machine/1ma6.cpp2
-rw-r--r--src/devices/machine/hp_dc100_tape.cpp27
-rw-r--r--src/devices/machine/hp_dc100_tape.h4
-rw-r--r--src/devices/machine/hp_taco.cpp7
-rw-r--r--src/devices/machine/hp_taco.h3
-rw-r--r--src/lib/formats/hti_tape.cpp34
-rw-r--r--src/lib/formats/hti_tape.h50
-rw-r--r--src/mame/drivers/hp2640.cpp85
-rw-r--r--src/mame/drivers/hp9845.cpp3
-rw-r--r--src/mame/machine/hp2640_tape.cpp569
-rw-r--r--src/mame/machine/hp2640_tape.h96
-rw-r--r--src/mame/machine/hp9825_tape.cpp2
-rw-r--r--src/tools/imgtool/modules/hp85_tape.cpp2
-rw-r--r--src/tools/imgtool/modules/hp9845_tape.cpp2
15 files changed, 833 insertions, 55 deletions
diff --git a/scripts/target/mame/mess.lua b/scripts/target/mame/mess.lua
index f4d9214f320..aac794dddcc 100644
--- a/scripts/target/mame/mess.lua
+++ b/scripts/target/mame/mess.lua
@@ -2601,6 +2601,8 @@ files {
MAME_DIR .. "src/mame/drivers/hp2100.cpp",
MAME_DIR .. "src/mame/drivers/hp2620.cpp",
MAME_DIR .. "src/mame/drivers/hp700.cpp",
+ MAME_DIR .. "src/mame/machine/hp2640_tape.cpp",
+ MAME_DIR .. "src/mame/machine/hp2640_tape.h",
MAME_DIR .. "src/mame/drivers/hp2640.cpp",
MAME_DIR .. "src/mame/drivers/hp95lx.cpp",
MAME_DIR .. "src/mame/drivers/hp9825.cpp",
diff --git a/src/devices/machine/1ma6.cpp b/src/devices/machine/1ma6.cpp
index 284088a9f00..621e6aa6960 100644
--- a/src/devices/machine/1ma6.cpp
+++ b/src/devices/machine/1ma6.cpp
@@ -341,7 +341,7 @@ void hp_1ma6_device::device_add_mconfig(machine_config &config)
m_tape->set_acceleration(ACCELERATION);
m_tape->set_set_points(SLOW_SPEED , FAST_SPEED);
m_tape->set_tick_size(TACH_TICK_LEN);
- m_tape->set_bits_per_word(16);
+ m_tape->set_image_format(hti_format_t::HTI_DELTA_MOD_16_BITS);
m_tape->set_go_threshold(MOVING_THRESHOLD);
m_tape->cart_out().set(FUNC(hp_1ma6_device::cart_out_w));
m_tape->hole().set(FUNC(hp_1ma6_device::hole_w));
diff --git a/src/devices/machine/hp_dc100_tape.cpp b/src/devices/machine/hp_dc100_tape.cpp
index a9cbb925163..2c4b55e0156 100644
--- a/src/devices/machine/hp_dc100_tape.cpp
+++ b/src/devices/machine/hp_dc100_tape.cpp
@@ -84,6 +84,7 @@ hp_dc100_tape_device::hp_dc100_tape_device(const machine_config &mconfig, const
, m_motion_handler(*this)
, m_rd_bit_handler(*this)
, m_wr_bit_handler(*this)
+ , m_unit_name()
, m_image()
, m_image_dirty(false)
{
@@ -128,7 +129,14 @@ std::string hp_dc100_tape_device::call_display()
return buffer;
}
- char track = m_track ? 'B' : 'A';
+ if (!m_unit_name.empty()) {
+ buffer += m_unit_name;
+ buffer += " ";
+ }
+
+ if (m_image.no_of_tracks() > 1) {
+ buffer += m_track ? "B " : "A ";
+ }
char r_w = m_current_op == OP_WRITE || m_current_op == OP_ERASE ? 'W' : 'R';
char m1;
char m2;
@@ -143,7 +151,7 @@ std::string hp_dc100_tape_device::call_display()
int pos_in = get_approx_pos() / hti_format_t::ONE_INCH_POS;
- buffer = string_format("%c %c %c%c [%04d/1824]" , track , r_w , m1 , m2 , pos_in);
+ buffer += string_format("%c %c%c [%04d/1824]" , r_w , m1 , m2 , pos_in);
return buffer;
}
@@ -169,9 +177,9 @@ void hp_dc100_tape_device::set_tick_size(hti_format_t::tape_pos_t size)
m_tick_size = size;
}
-void hp_dc100_tape_device::set_bits_per_word(unsigned bits)
+void hp_dc100_tape_device::set_image_format(hti_format_t::image_format_t fmt)
{
- m_image.set_bits_per_word(bits);
+ m_image.set_image_format(fmt);
}
void hp_dc100_tape_device::set_go_threshold(double threshold)
@@ -179,9 +187,14 @@ void hp_dc100_tape_device::set_go_threshold(double threshold)
m_go_threshold = threshold;
}
+void hp_dc100_tape_device::set_name(const std::string& name)
+{
+ m_unit_name = name;
+}
+
void hp_dc100_tape_device::set_track_no(unsigned track)
{
- if (m_track != track) {
+ if (m_track != track && track < m_image.no_of_tracks()) {
LOG_DBG("Setting track %u (op=%d)\n" , track , static_cast<int>(m_current_op));
auto saved_op = m_current_op;
if (m_current_op != OP_IDLE) {
@@ -434,7 +447,7 @@ void hp_dc100_tape_device::time_to_next_gap(hti_format_t::tape_pos_t min_gap_siz
hti_format_t::track_iterator_t it;
found = m_image.next_data(get_track_no() , tmp , fwd , true , it);
if (found) {
- tmp = hti_format_t::farthest_end(it , !fwd);
+ tmp = m_image.farthest_end(it , !fwd);
}
}
if (found && m_image.next_gap(get_track_no() , tmp , fwd , min_gap_size)) {
@@ -875,7 +888,7 @@ void hp_dc100_tape_device::load_rd_word()
m_bit_idx = 0;
}
// This is actually the nearest end (dir is inverted)
- m_rw_pos = m_next_bit_pos = hti_format_t::farthest_end(m_rd_it , !fwd);
+ m_rw_pos = m_next_bit_pos = m_image.farthest_end(m_rd_it , !fwd);
// Compute end of bit cell
hti_format_t::tape_pos_t bit_len = m_image.bit_length(BIT(m_rd_it->second , m_bit_idx));
if (!fwd) {
diff --git a/src/devices/machine/hp_dc100_tape.h b/src/devices/machine/hp_dc100_tape.h
index 167bb953b10..2d3a2ea0617 100644
--- a/src/devices/machine/hp_dc100_tape.h
+++ b/src/devices/machine/hp_dc100_tape.h
@@ -44,8 +44,9 @@ public:
void set_acceleration(double accel);
void set_set_points(double slow_sp , double fast_sp);
void set_tick_size(hti_format_t::tape_pos_t size);
- void set_bits_per_word(unsigned bits);
+ void set_image_format(hti_format_t::image_format_t fmt);
void set_go_threshold(double threshold);
+ void set_name(const std::string& name);
// Commands
void set_track_no(unsigned track);
@@ -123,6 +124,7 @@ private:
double m_fast_set_point;
hti_format_t::tape_pos_t m_tick_size;
double m_go_threshold;
+ std::string m_unit_name;
// State
hti_format_t::tape_pos_t m_tape_pos;
diff --git a/src/devices/machine/hp_taco.cpp b/src/devices/machine/hp_taco.cpp
index 6760a7eda01..64e1cd9588c 100644
--- a/src/devices/machine/hp_taco.cpp
+++ b/src/devices/machine/hp_taco.cpp
@@ -293,6 +293,11 @@ hp_taco_device::hp_taco_device(const machine_config &mconfig, const char *tag, d
{
}
+void hp_taco_device::set_name(const std::string& name)
+{
+ m_tape->set_name(name);
+}
+
void hp_taco_device::reg_w(offs_t offset, uint16_t data)
{
LOG_REG("wr R%u = %04x\n", 4 + offset , data);
@@ -538,7 +543,7 @@ void hp_taco_device::device_add_mconfig(machine_config &config)
m_tape->set_acceleration(ACCELERATION);
m_tape->set_set_points(SLOW_SPEED , FAST_SPEED);
m_tape->set_tick_size(TACH_TICK_LEN);
- m_tape->set_bits_per_word(16);
+ m_tape->set_image_format(hti_format_t::HTI_DELTA_MOD_16_BITS);
m_tape->set_go_threshold(MOVING_THRESHOLD);
m_tape->cart_out().set(FUNC(hp_taco_device::cart_out_w));
m_tape->hole().set(FUNC(hp_taco_device::hole_w));
diff --git a/src/devices/machine/hp_taco.h b/src/devices/machine/hp_taco.h
index 0ea70122b7f..243b79e98ba 100644
--- a/src/devices/machine/hp_taco.h
+++ b/src/devices/machine/hp_taco.h
@@ -27,6 +27,9 @@ public:
auto flg() { return m_flg_handler.bind(); }
auto sts() { return m_sts_handler.bind(); }
+ // Set unit name
+ void set_name(const std::string& name);
+
// Register read/write
void reg_w(offs_t offset, uint16_t data);
uint16_t reg_r(offs_t offset);
diff --git a/src/lib/formats/hti_tape.cpp b/src/lib/formats/hti_tape.cpp
index 29bab6cd51c..7f77e3f99df 100644
--- a/src/lib/formats/hti_tape.cpp
+++ b/src/lib/formats/hti_tape.cpp
@@ -12,7 +12,8 @@
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,7 +42,7 @@ 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();
}
@@ -52,13 +53,15 @@ bool hti_format_t::load_tape(io_generic *io)
io_generic_read(io, tmp, 0, 4);
auto magic = pick_integer_be(tmp , 0 , 4);
- if (magic != FILE_MAGIC && magic != OLD_FILE_MAGIC) {
+ 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;
}
uint64_t offset = 4;
- for (tape_track_t& track : m_tracks) {
+ for (unsigned i = 0; i < no_of_tracks(); i++) {
+ tape_track_t& track = m_tracks[ i ];
if (!load_track(io , offset , track , magic == OLD_FILE_MAGIC)) {
clear_tape();
return false;
@@ -72,12 +75,13 @@ void hti_format_t::save_tape(io_generic *io)
{
uint8_t tmp[ 4 ];
- place_integer_be(tmp, 0, 4, FILE_MAGIC);
+ place_integer_be(tmp, 0, 4, m_img_format == HTI_MANCHESTER_MOD ? FILE_MAGIC_MANCHESTER : FILE_MAGIC_DELTA);
io_generic_write(io, tmp, 0, 4);
uint64_t offset = 4;
- for (const tape_track_t& track : m_tracks) {
+ 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;
@@ -105,7 +109,7 @@ void hti_format_t::clear_tape()
}
}
-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;
@@ -117,10 +121,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);
@@ -190,7 +194,7 @@ void hti_format_t::write_word(unsigned track_no , tape_pos_t start , tape_word_t
// 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--;
}
@@ -414,7 +418,7 @@ bool hti_format_t::load_track(io_generic *io , uint64_t& offset , tape_track_t&
if (!old_format) {
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 +433,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,7 +462,7 @@ 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);
}
@@ -483,12 +487,12 @@ void hti_format_t::dump_sequence(io_generic *io , uint64_t& offset , tape_track_
}
}
-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;
diff --git a/src/lib/formats/hti_tape.h b/src/lib/formats/hti_tape.h
index 72eb85d55cc..7725b87fa2a 100644
--- a/src/lib/formats/hti_tape.h
+++ b/src/lib/formats/hti_tape.h
@@ -4,8 +4,8 @@
"HTI" format
- Format of images of DC-100 tape cassettes as used in HP 9825,
- HP 9845 and HP 85 systems.
+ Format of images of DC-100 tape cassettes as used in HP 264x,
+ HP 9825, HP 9845 and HP 85 systems.
*********************************************************************/
#ifndef MAME_FORMATS_HTI_TAPE_H
@@ -34,11 +34,15 @@ public:
// 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;
+ // Length of bits in delta modulation
// Length of 0 bits at slow tape speed: 1/(35200 Hz)
- static constexpr tape_pos_t ZERO_BIT_LEN = 619;
-
+ static constexpr tape_pos_t DELTA_ZERO_BIT_LEN = 619;
// Length of 1 bits at slow tape speed: 1.75 times ZERO_BIT_LEN
- static constexpr tape_pos_t ONE_BIT_LEN = 1083;
+ static constexpr tape_pos_t DELTA_ONE_BIT_LEN = 1083;
+ // Length of bits in Manchester modulation
+ // By "bits" here we mean each of the halves of data bit cells
+ // Zeros & ones have same length
+ static constexpr tape_pos_t MANCHESTER_BIT_LEN = 621;
// Words stored on tape
typedef uint16_t tape_word_t;
@@ -49,20 +53,38 @@ public:
// Iterator to access words on tape
typedef tape_track_t::iterator track_iterator_t;
- // Set no. of bits per word (needed when loading old format)
- void set_bits_per_word(unsigned bits) { m_bits_per_word = bits; }
+ // Set image format
+ enum image_format_t {
+ // Delta modulation, 16 bits per word, 2 tracks per cartridge
+ // HP 9845 & HP 85
+ HTI_DELTA_MOD_16_BITS,
+ // Delta modulation, 17 bits per word, 2 tracks per cartridge
+ // HP 9825
+ HTI_DELTA_MOD_17_BITS,
+ // Manchester modulation, 1 track per cartridge
+ // HP 264x
+ HTI_MANCHESTER_MOD
+ };
+
+ void set_image_format(image_format_t fmt) { m_img_format = fmt; }
+
+ // Return number of tracks
+ unsigned no_of_tracks() const { return m_img_format == HTI_MANCHESTER_MOD ? 1 : 2; }
bool load_tape(io_generic *io);
void save_tape(io_generic *io);
void clear_tape();
// Return physical length of a bit on tape
- static constexpr tape_pos_t bit_length(bool bit) { return bit ? ONE_BIT_LEN : ZERO_BIT_LEN; }
+ tape_pos_t bit_length(bool bit) const
+ {
+ return m_img_format == HTI_MANCHESTER_MOD ? MANCHESTER_BIT_LEN : (bit ? DELTA_ONE_BIT_LEN : DELTA_ZERO_BIT_LEN);
+ }
// Return physical length of a 16-bit word on tape
- static tape_pos_t word_length(tape_word_t w);
+ tape_pos_t word_length(tape_word_t w) const;
- static tape_pos_t farthest_end(const track_iterator_t& it , bool forward);
+ tape_pos_t farthest_end(const track_iterator_t& it , bool forward) const;
static bool pos_offset(tape_pos_t& pos , bool forward , tape_pos_t offset);
@@ -102,14 +124,14 @@ public:
private:
// Content of tape tracks
tape_track_t m_tracks[ 2 ];
- // Bits per word in old format
- unsigned m_bits_per_word;
+ // Image format
+ image_format_t m_img_format;
bool load_track(io_generic *io , uint64_t& offset , tape_track_t& track , bool old_format);
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);
+ tape_pos_t word_end_pos(const track_iterator_t& it) const;
+ void adjust_it(tape_track_t& track , track_iterator_t& it , tape_pos_t pos) const;
static void ensure_a_lt_b(tape_pos_t& a , tape_pos_t& b);
};
diff --git a/src/mame/drivers/hp2640.cpp b/src/mame/drivers/hp2640.cpp
index 13dba354de9..a33f75820b8 100644
--- a/src/mame/drivers/hp2640.cpp
+++ b/src/mame/drivers/hp2640.cpp
@@ -23,10 +23,10 @@
// was used with APL\3000 on the HP 3000 Series II/III computers from 1976
// into the 1980s. I recently got APL\3000 running under simulation so this
// is an attempt to recreate the whole experience with the APL characters
-// on the terminal device designed for it. The HP2641A is basically a 2641
-// with a few altered ROMs (many are identical to the 2641) and one
+// on the terminal device designed for it. The HP2641A is basically a 2645
+// with a few altered ROMs (many are identical to the 2645) and one
// additional 2K ROM for which a second CTL PCA was required that was not
-// needed in the 2641.
+// needed in the 2645.
//
// This driver emulates the HP2641A model, as composed of the following cards:
// - 02640-60123 Keyboard interface
@@ -39,6 +39,8 @@
// - 02640-60192 Second CTL PCA with one additional 2K ROM and 256b SRAM.
// - 02640-60065 4k DRAM (4 of these for a 16k total)
// - 02640-60086 Asynchronous data comm
+// - 02640-60137 CTU interface
+// - 02640-60032 Read/write PCA
//
// The following table summarizes the emulated character sets. The 2641A only
// had room for a single optional character set (line drawing here) after the
@@ -67,6 +69,8 @@
// - 02640-60192 Control storage (firmware ROMs & 256-byte SRAM)
// - 02640-60065 4k DRAM (4 of these for a 16k total)
// - 02640-60086 Asynchronous data comm
+// - 02640-60137 CTU interface
+// - 02640-60032 Read/write PCA
//
// The following table summarizes the emulated character sets.
//
@@ -97,7 +101,6 @@
// - LEDs are not output.
// - RESET key is not implemented.
// - A few TODOs here & there.
-// - DC100 data cassettes are not emulated.
#include "emu.h"
#include "screen.h"
@@ -106,6 +109,7 @@
#include "bus/rs232/rs232.h"
#include "machine/ay31015.h"
#include "machine/clock.h"
+#include "machine/hp2640_tape.h"
#include "sound/beep.h"
#include "emupal.h"
#include "speaker.h"
@@ -215,6 +219,10 @@ protected:
TIMER_DEVICE_CALLBACK_MEMBER(timer_beep_exp);
+ DECLARE_WRITE_LINE_MEMBER(tape_irq_w);
+
+ uint8_t poll_r();
+
void cpu_mem_map(address_map &map);
void cpu_io_map(address_map &map);
@@ -233,10 +241,13 @@ protected:
required_device<clock_device> m_uart_clock;
required_device<beep_device> m_beep;
required_device<timer_device> m_timer_beep;
+ required_device<hp2640_tape_device> m_tapes;
+ memory_view m_io_view;
uint8_t m_mode_byte;
bool m_timer_irq;
bool m_datacom_irq;
+ bool m_tape_irq;
// Character generators
required_region_ptr_array<uint8_t , 4> m_chargen;
@@ -297,6 +308,8 @@ hp2640_base_state::hp2640_base_state(const machine_config &mconfig, device_type
m_uart_clock(*this , "uart_clock"),
m_beep(*this , "beep"),
m_timer_beep(*this , "timer_beep"),
+ m_tapes(*this , "tapes"),
+ m_io_view(*this , "io_view"),
m_chargen(*this , "chargen%u" , 0),
m_chargen_set{ m_cg_0 , m_cg_1 , m_cg_2 , m_cg_3}
{
@@ -310,6 +323,7 @@ void hp2640_base_state::machine_start()
save_item(NAME(m_mode_byte));
save_item(NAME(m_timer_irq));
save_item(NAME(m_datacom_irq));
+ save_item(NAME(m_tape_irq));
}
void hp2640_base_state::machine_reset()
@@ -317,6 +331,7 @@ void hp2640_base_state::machine_reset()
m_mode_byte = 0;
m_timer_irq = false;
m_datacom_irq = false;
+ m_tape_irq = false;
m_timer_10ms->reset();
update_irq();
m_blanking = true;
@@ -339,7 +354,10 @@ IRQ_CALLBACK_MEMBER(hp2640_base_state::irq_callback)
uint8_t res;
// Encode interrupts in restart instruction (in order of decreasing priority)
- if (m_datacom_irq && !BIT(m_mode_byte , 4)) {
+ if (m_tape_irq) {
+ // RST 5
+ res = 0xef;
+ } else if (m_datacom_irq && !BIT(m_mode_byte , 4)) {
// RST 4
res = 0xe7;
} else if (m_timer_irq && !BIT(m_mode_byte , 5)) {
@@ -366,6 +384,8 @@ void hp2640_base_state::mode_byte_w(uint8_t data)
m_timer_irq = false;
}
update_irq();
+
+ m_io_view.select(BIT(m_mode_byte , 6));
}
TIMER_DEVICE_CALLBACK_MEMBER(hp2640_base_state::timer_10ms_exp)
@@ -549,9 +569,23 @@ TIMER_DEVICE_CALLBACK_MEMBER(hp2640_base_state::timer_beep_exp)
m_beep->set_state(0);
}
+WRITE_LINE_MEMBER(hp2640_base_state::tape_irq_w)
+{
+ m_tape_irq = state;
+ update_irq();
+}
+
+uint8_t hp2640_base_state::poll_r()
+{
+ uint8_t res = m_tapes->poll_r();
+ LOG("POLL %02x\n" , res);
+ return res;
+}
+
void hp2640_base_state::update_irq()
{
- bool state = (m_datacom_irq && !BIT(m_mode_byte , 4)) ||
+ bool state = m_tape_irq ||
+ (m_datacom_irq && !BIT(m_mode_byte , 4)) ||
(m_timer_irq && !BIT(m_mode_byte , 5));
m_cpu->set_input_line(I8085_INTR_LINE , state);
}
@@ -1012,19 +1046,40 @@ void hp2640_base_state::cpu_mem_map(address_map &map)
{
map.unmap_value_low();
map(0x0000, 0x57ff).rom();
- map(0x8100, 0x8100).r(m_uart, FUNC(ay51013_device::receive));
- map(0x8120, 0x8120).r(FUNC(hp2640_base_state::async_status_r));
+ map(0x8000, 0x8fff).view(m_io_view);
+
+ // View 0 is for normal I/O
+ // View 1 is for poll read
+ // Writing is independent of poll state
+ m_io_view[ 0 ](0x0100, 0x0100).r(m_uart, FUNC(ay51013_device::receive));
+ m_io_view[ 0 ](0x0120, 0x0120).r(FUNC(hp2640_base_state::async_status_r));
+
map(0x8140, 0x8140).w(FUNC(hp2640_base_state::async_control_w));
map(0x8160, 0x8160).w(m_uart, FUNC(ay51013_device::transmit));
map(0x8300, 0x8300).w(FUNC(hp2640_base_state::kb_led_w));
- map(0x8300, 0x830d).r(FUNC(hp2640_base_state::kb_r));
- map(0x830e, 0x830e).r(FUNC(hp2640_base_state::switches_ah_r));
- map(0x830f, 0x830f).r(FUNC(hp2640_base_state::datacomm_sw_r));
+
+ m_io_view[ 0 ](0x0300, 0x030d).r(FUNC(hp2640_base_state::kb_r));
+ m_io_view[ 0 ](0x030e, 0x030e).r(FUNC(hp2640_base_state::switches_ah_r));
+ m_io_view[ 0 ](0x030f, 0x030f).r(FUNC(hp2640_base_state::datacomm_sw_r));
+
map(0x8320, 0x8320).w(FUNC(hp2640_base_state::kb_prev_w));
- map(0x8380, 0x8380).rw(FUNC(hp2640_base_state::switches_jr_r), FUNC(hp2640_base_state::kb_reset_w));
- map(0x83a0, 0x83a0).r(FUNC(hp2640_base_state::switches_sz_r));
+ map(0x8380, 0x8380).w(FUNC(hp2640_base_state::kb_reset_w));
+
+ m_io_view[ 0 ](0x0380, 0x0380).r(FUNC(hp2640_base_state::switches_jr_r));
+ m_io_view[ 0 ](0x03a0, 0x03a0).r(FUNC(hp2640_base_state::switches_sz_r));
+
map(0x8700, 0x8700).w(FUNC(hp2640_base_state::cx_w));
map(0x8720, 0x8720).w(FUNC(hp2640_base_state::cy_w));
+ map(0x8b00, 0x8b00).w(m_tapes, FUNC(hp2640_tape_device::command_w));
+
+ m_io_view[ 0 ](0x0b00, 0x0b00).r(m_tapes, FUNC(hp2640_tape_device::status_r));
+
+ map(0x8b20, 0x8b20).w(m_tapes, FUNC(hp2640_tape_device::data_w));
+
+ m_io_view[ 0 ](0x0b20, 0x0b20).r(m_tapes, FUNC(hp2640_tape_device::data_r));
+
+ m_io_view[ 1 ](0x0000, 0x0fff).r(FUNC(hp2640_base_state::poll_r));
+
map(0x9100, 0x91ff).ram();
map(0xc000, 0xffff).ram();
}
@@ -1075,6 +1130,10 @@ void hp2640_base_state::hp2640_base(machine_config &config)
SPEAKER(config, "mono").front_center();
BEEP(config, m_beep, BEEP_FREQUENCY).add_route(ALL_OUTPUTS, "mono", 1.00);
TIMER(config, m_timer_beep).configure_generic(FUNC(hp2640_base_state::timer_beep_exp));
+
+ // Tape drives
+ HP2640_TAPE(config , m_tapes , SYS_CLOCK);
+ m_tapes->irq().set(FUNC(hp2640_base_state::tape_irq_w));
}
// ************
diff --git a/src/mame/drivers/hp9845.cpp b/src/mame/drivers/hp9845.cpp
index ce87c4ca60c..ed279a0d64c 100644
--- a/src/mame/drivers/hp9845.cpp
+++ b/src/mame/drivers/hp9845.cpp
@@ -451,6 +451,9 @@ void hp9845_base_state::machine_start()
m_screen->register_screen_bitmap(m_bitmap);
+ m_t15->set_name("T15");
+ m_t14->set_name("T14");
+
// setup RAM dynamically for -ramsize
// 0K..64K
setup_ram_block(0 , 0);
diff --git a/src/mame/machine/hp2640_tape.cpp b/src/mame/machine/hp2640_tape.cpp
new file mode 100644
index 00000000000..ad22cc1c884
--- /dev/null
+++ b/src/mame/machine/hp2640_tape.cpp
@@ -0,0 +1,569 @@
+// license:BSD-3-Clause
+// copyright-holders: F. Ulivi
+/*********************************************************************
+
+ hp2640_tape.cpp
+
+ Tape subsystem of HP264x terminals
+
+ Tape subsystem is composed of two DC100 drives and two cards:
+ 02640-60137 CTU interface
+ 02640-60032 Read/write PCA
+
+ Data is recorded on a single track with Manchester modulation.
+ This modulation produces a maximum flux transition
+ density of 1600 transitions per inch. Bytes are recorded from
+ LSB to MSB. A "0" bit is recorded as 10, a "1" as 01.
+ HP264x tapes are not compatible with HP9825, HP85 & HP9845 systems
+ despite using the same DC100 cartridges. The latter systems record
+ data with a different modulation (delta-distance) and use two tracks.
+
+ Format of records on tape:
+ 0..3 Preamble = 00 00 00 80
+ 4..5 Length of record, MSB first
+ 6..x Record data
+ x+1 Checksum
+ x+2..x+5 Postamble = 01 00 00 00 (which is just the preamble in
+ reverse)
+
+ Most significant bit of record length is set to 1 when starting
+ a file header. In this case there's just one byte of data carrying
+ the file number.
+
+ Gaps of about 0.85" separate data records. A file header is
+ surrounded by two gaps of about 1.7" length. End of data on tape
+ is marked by 11" of gap.
+
+ Reference docs:
+
+ 13255-91032, Cartridge tape module
+ 13255-91137, Extended CTU interface module
+
+*********************************************************************/
+
+#include "emu.h"
+#include "hp2640_tape.h"
+
+// Debugging
+#include "logmacro.h"
+#undef VERBOSE
+#define VERBOSE 0
+//#define VERBOSE (LOG_GENERAL)
+
+// Bit manipulation
+namespace {
+ template<typename T> constexpr T BIT_MASK(unsigned n)
+ {
+ return (T)1U << n;
+ }
+
+ template<typename T> void BIT_CLR(T& w , unsigned n)
+ {
+ w &= ~BIT_MASK<T>(n);
+ }
+
+ template<typename T> void BIT_SET(T& w , unsigned n)
+ {
+ w |= BIT_MASK<T>(n);
+ }
+}
+
+// Timers
+enum {
+ GAP_TMR_ID,
+ CELL_TMR_ID
+};
+
+// Constants
+constexpr double FAST_SPEED = 60.0; // Fast speed: 60 ips
+constexpr double SLOW_SPEED = 10.0; // Slow speed: 10 ips
+constexpr double MOVING_THRESHOLD = 1.0; // Tape is moving when speed > 1.0 ips
+constexpr double ACCELERATION = 2000.0; // Acceleration when speed set point is changed: 2000 ips^2
+// 58.4 tachometer pulses per inch
+constexpr hti_format_t::tape_pos_t TACH_TICK_LENGTH = static_cast<hti_format_t::tape_pos_t>(hti_format_t::ONE_INCH_POS / 58.4);
+constexpr uint8_t MODULUS_RESET = 0b10110011; // Reset value of modulus: -77
+constexpr unsigned GAP_TIME_MS = 1; // 1 ms to detect start/end of gaps
+
+// Bits in command register
+enum : unsigned {
+ CMD_REG_LEFT_LIGHT_BIT = 7, // Light of left drive (1)
+ CMD_REG_RIGHT_LIGHT_BIT = 6,// Light of right drive (1)
+ CMD_REG_GAP_WRITE_BIT = 5, // Write gap (1)
+ CMD_REG_LEFT_SEL_BIT = 4, // Select left (1) or right (0) drive
+ CMD_REG_WRITE_BIT = 3, // Read (0) or Write (1)
+ CMD_REG_SPEED_BIT = 2, // Slow (0) or Fast (1)
+ CMD_REG_DIR_BIT = 1, // Reverse (0) or Forward (1)
+ CMD_REG_RUN_BIT = 0 // Stop (0) or Run (1)
+};
+
+// Bits in status register
+enum : unsigned {
+ STAT_REG_TACH_INT_BIT = 7, // Tachometer tick interrupt (1)
+ STAT_REG_BYTE_READY_BIT = 6,// Byte ready (1)
+ STAT_REG_GAP_BIT = 5, // Gap detected (1)
+ STAT_REG_HOLE_INT_BIT = 4, // Hole interrupt (1)
+ STAT_REG_TACH_DIV2_BIT = 3, // Tach/2
+ STAT_REG_RIP_BIT = 2, // Record in progress (1)
+ STAT_REG_RIGHT_CIN_BIT = 1, // Right cartridge in (1)
+ STAT_REG_LEFT_CIN_BIT = 0 // Left cartridge in (1)
+};
+
+// device type definition
+DEFINE_DEVICE_TYPE(HP2640_TAPE, hp2640_tape_device, "hp2640_tape" , "HP2640 tape subsystem")
+
+hp2640_tape_device::hp2640_tape_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
+ : device_t(mconfig, HP2640_TAPE, tag, owner, clock)
+ , m_irq_handler(*this)
+ , m_led0_handler(*this)
+ , m_led1_handler(*this)
+ , m_drives(*this , "unit%u" , 0)
+{
+}
+
+void hp2640_tape_device::command_w(uint8_t cmd)
+{
+ auto cmd_reg_diff = m_cmd_reg ^ cmd;
+ m_cmd_reg = cmd;
+
+ LOG("CMD=%02x D=%02x\n" , m_cmd_reg , cmd_reg_diff);
+
+ m_led0_handler(BIT(m_cmd_reg , CMD_REG_LEFT_LIGHT_BIT));
+ m_led1_handler(BIT(m_cmd_reg , CMD_REG_RIGHT_LIGHT_BIT));
+
+ if (BIT(cmd_reg_diff , CMD_REG_LEFT_SEL_BIT)) {
+ // Drive selection changed, deselect old drive
+ m_drives[ m_selected_drive ]->set_op(hp_dc100_tape_device::OP_IDLE);
+ m_drives[ m_selected_drive ]->set_speed_setpoint(hp_dc100_tape_device::SP_STOP , false);
+ m_selected_drive = BIT(m_cmd_reg , CMD_REG_LEFT_SEL_BIT) ? 0 : 1;
+ }
+
+ if (cmd_reg_diff &
+ (BIT_MASK<uint8_t>(CMD_REG_RUN_BIT) |
+ BIT_MASK<uint8_t>(CMD_REG_DIR_BIT) |
+ BIT_MASK<uint8_t>(CMD_REG_SPEED_BIT) |
+ BIT_MASK<uint8_t>(CMD_REG_WRITE_BIT) |
+ BIT_MASK<uint8_t>(CMD_REG_LEFT_SEL_BIT) |
+ BIT_MASK<uint8_t>(CMD_REG_GAP_WRITE_BIT))) {
+ // Drive selection and/or speed and/or r/w operation changed
+
+ // ISF == true when running forward at slow speed
+ m_isf = (m_cmd_reg & (BIT_MASK<uint8_t>(CMD_REG_RUN_BIT) | BIT_MASK<uint8_t>(CMD_REG_DIR_BIT) | BIT_MASK<uint8_t>(CMD_REG_SPEED_BIT))) ==
+ (BIT_MASK<uint8_t>(CMD_REG_RUN_BIT) | BIT_MASK<uint8_t>(CMD_REG_DIR_BIT));
+
+ if (!BIT(m_cmd_reg , CMD_REG_WRITE_BIT)) {
+ m_current_op = hp_dc100_tape_device::OP_READ;
+ } else if (m_isf && BIT(m_cmd_reg , CMD_REG_WRITE_BIT) && !BIT(m_cmd_reg , CMD_REG_GAP_WRITE_BIT)) {
+ m_current_op = hp_dc100_tape_device::OP_WRITE;
+ } else if (BIT(m_cmd_reg , CMD_REG_WRITE_BIT) && BIT(m_cmd_reg , CMD_REG_GAP_WRITE_BIT)) {
+ m_current_op = hp_dc100_tape_device::OP_ERASE;
+ } else {
+ m_current_op = hp_dc100_tape_device::OP_IDLE;
+ }
+
+ if (!set_speed()) {
+ start_rd_wr();
+ }
+ }
+}
+
+uint8_t hp2640_tape_device::status_r()
+{
+ uint8_t res = 0;
+
+ if (m_tach_latch) {
+ BIT_SET(res , 7);
+ }
+ if (m_byte_ready) {
+ BIT_SET(res , 6);
+ }
+ if (m_gap) {
+ BIT_SET(res , 5);
+ }
+ if (m_hole_latch) {
+ BIT_SET(res , 4);
+ }
+ if (m_tach_div2) {
+ BIT_SET(res , 3);
+ }
+ if (m_rip) {
+ BIT_SET(res , 2);
+ }
+ if (!m_drives[ 1 ]->cart_out_r()) {
+ BIT_SET(res , 1);
+ }
+ if (!m_drives[ 0 ]->cart_out_r()) {
+ BIT_SET(res , 0);
+ }
+
+ m_tach_latch = false;
+ m_hole_latch = false;
+ update_irq();
+
+ LOG("STS=%02x\n" , res);
+ return res;
+}
+
+void hp2640_tape_device::data_w(uint8_t data)
+{
+ LOG("DATA W=%02x\n" , data);
+ m_data_rd = data;
+ m_byte_ready = false;
+ update_irq();
+}
+
+uint8_t hp2640_tape_device::data_r()
+{
+ m_byte_ready = false;
+ update_irq();
+
+ LOG("DATA R=%02x\n" , m_data_rd);
+ return m_data_rd;
+}
+
+uint8_t hp2640_tape_device::poll_r() const
+{
+ return m_irq ? 0x80 : 0x00;
+}
+
+void hp2640_tape_device::device_add_mconfig(machine_config &config)
+{
+ for (unsigned i = 0; i < 2; i++) {
+ auto& finder = m_drives[ i ];
+
+ HP_DC100_TAPE(config , finder , 0);
+ // Acceleration: 2000 in/s^2
+ finder->set_acceleration(ACCELERATION);
+ // Slow speed: 10 ips, Fast speed: 60 ips
+ finder->set_set_points(SLOW_SPEED , FAST_SPEED);
+ // 58.4 ticks per inch
+ finder->set_tick_size(TACH_TICK_LENGTH);
+ // Manchester encoded data
+ finder->set_image_format(hti_format_t::HTI_MANCHESTER_MOD);
+ // Moving when speed is >1 ips
+ finder->set_go_threshold(MOVING_THRESHOLD);
+ // Unit name: U0/U1
+ finder->set_name(string_format("U%u" , i));
+
+ finder->hole().set([this , i](int state) { hole_w(i , state); });
+ finder->tacho_tick().set([this , i](int state) { tacho_tick_w(i , state); });
+ finder->motion_event().set([this , i](int state) { motion_w(i , state); });
+ finder->rd_bit().set([this , i](int state) { rd_bit_w(i , state); });
+ finder->wr_bit().set([this , i](int state) { return wr_bit_r(i); });
+ }
+}
+
+void hp2640_tape_device::device_start()
+{
+ m_irq_handler.resolve_safe();
+ m_led0_handler.resolve_safe();
+ m_led1_handler.resolve_safe();
+
+ m_gap_timer = timer_alloc(GAP_TMR_ID);
+ m_cell_timer = timer_alloc(CELL_TMR_ID);
+
+ save_item(NAME(m_selected_drive));
+ save_item(NAME(m_cmd_reg));
+ save_item(NAME(m_data_rd));
+ save_item(NAME(m_data_sr));
+ save_item(NAME(m_modulus));
+ save_item(NAME(m_cell_cnt));
+ save_item(NAME(m_bit_cnt));
+ save_item(NAME(m_tach_div2));
+ save_item(NAME(m_tach_latch));
+ save_item(NAME(m_hole_latch));
+ save_item(NAME(m_byte_ready));
+ save_item(NAME(m_irq));
+ save_item(NAME(m_bit_sync));
+ save_item(NAME(m_wr_bit));
+ save_item(NAME(m_last_rd_bit));
+ save_item(NAME(m_isf));
+ save_item(NAME(m_gap));
+ save_item(NAME(m_prev_gap));
+ save_item(NAME(m_rip));
+}
+
+void hp2640_tape_device::device_reset()
+{
+ m_selected_drive = 0;
+
+ // All bits set to 1 but 3 & 0
+ m_cmd_reg = ~0;
+ BIT_CLR(m_cmd_reg , CMD_REG_WRITE_BIT);
+ BIT_CLR(m_cmd_reg , CMD_REG_RUN_BIT);
+
+ m_modulus = MODULUS_RESET;
+ m_cell_cnt = 6;
+ m_bit_cnt = 8;
+ m_tach_div2 = false;
+ m_tach_latch = false;
+ m_hole_latch = false;
+ m_byte_ready = false;
+ m_irq = true;
+ m_bit_sync = false;
+ m_isf = false;
+ m_gap = true;
+ m_prev_gap = true;
+ m_rip = false;
+ m_current_op = hp_dc100_tape_device::OP_READ;
+ update_irq();
+
+ m_gap_timer->reset();
+ m_cell_timer->reset();
+}
+
+void hp2640_tape_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr)
+{
+ LOG("TMR %d @%s\n" , id , machine().time().to_string());
+
+ switch (id) {
+ case GAP_TMR_ID:
+ m_gap = !m_gap;
+ if (m_gap) {
+ set_gap();
+ } else {
+ LOG("GAP ENDS\n");
+ load_gap_timer();
+ if (m_isf) {
+ load_modulus();
+ }
+ }
+ break;
+
+ case CELL_TMR_ID:
+ if (m_current_op == hp_dc100_tape_device::OP_READ) {
+ m_cell_cnt++;
+ if (m_cell_cnt == 15) {
+ restart_cell_cnt();
+ } else {
+ load_modulus();
+ }
+ }
+ break;
+
+ default:
+ break;
+ }
+}
+
+void hp2640_tape_device::hole_w(unsigned drive , int state)
+{
+ if (state && drive == m_selected_drive) {
+ LOG("HOLE\n");
+ m_hole_latch = true;
+ BIT_CLR(m_cmd_reg, CMD_REG_RUN_BIT);
+ set_speed();
+ update_irq();
+ }
+}
+
+void hp2640_tape_device::tacho_tick_w(unsigned drive , int state)
+{
+ if (state && drive == m_selected_drive) {
+ m_tach_div2 = !m_tach_div2;
+ if (m_tach_div2) {
+ LOG("HALF TICK\n");
+ m_tach_latch = true;
+ update_irq();
+ }
+ }
+}
+
+void hp2640_tape_device::motion_w(unsigned drive , int state)
+{
+ if (state && drive == m_selected_drive) {
+ LOG("MOTION\n");
+ start_rd_wr();
+ }
+}
+
+void hp2640_tape_device::rd_bit_w(unsigned drive , int state)
+{
+ if (drive == m_selected_drive) {
+ bool rd_bit = state != 0;
+ bool transition = rd_bit != m_last_rd_bit;
+ m_last_rd_bit = rd_bit;
+
+ LOG("RD BIT %d TR %d GP %d IS %d CC %u MD %u @%s\n" , rd_bit , transition , m_gap , m_isf , m_cell_cnt , m_modulus , machine().time().to_string());
+
+ if (transition) {
+ if (m_gap) {
+ if (m_prev_gap) {
+ m_prev_gap = false;
+ load_gap_timer();
+ }
+ } else {
+ load_gap_timer();
+
+ if (m_isf) {
+ if (BIT(m_cell_cnt , 3)) {
+ // Adjust modulus by +/- 1
+ if (BIT(m_cell_cnt , 1)) {
+ m_modulus--;
+ } else {
+ m_modulus++;
+ }
+ }
+ bool prev_bit_sync = m_bit_sync;
+ if ((m_cell_cnt & 0xc) == 0xc) {
+ if (m_bit_sync) {
+ // Shift in read bit
+ LOG("BIT %d CNT %u\n" , rd_bit , m_bit_cnt);
+ m_data_sr >>= 1;
+ if (rd_bit) {
+ BIT_SET(m_data_sr, 7);
+ }
+ m_bit_cnt++;
+ if (m_bit_cnt == 16) {
+ LOG("RD DATA=%02x\n" , m_data_sr);
+ m_bit_cnt = 8;
+ m_data_rd = m_data_sr;
+ m_byte_ready = true;
+ update_irq();
+ }
+ } else if (rd_bit) {
+ // Sync achieved
+ LOG("SYNC\n");
+ m_bit_sync = true;
+ m_bit_cnt = 8;
+ m_byte_ready = true;
+ update_irq();
+ }
+ }
+ if (BIT(m_cell_cnt , 2) || !prev_bit_sync) {
+ restart_cell_cnt();
+ }
+ }
+ }
+ }
+ }
+}
+
+int hp2640_tape_device::wr_bit_r(unsigned drive)
+{
+ if (drive == m_selected_drive) {
+ // IRL the m_cell_cnt counts in [6..13] range
+ if (m_cell_cnt == 6) {
+ m_cell_cnt = 13;
+ m_wr_bit = !BIT(m_data_sr , 0);
+ } else if (m_cell_cnt == 13) {
+ m_cell_cnt = 6;
+ m_wr_bit = !m_wr_bit;
+ m_bit_cnt++;
+ if (m_bit_cnt == 16) {
+ m_data_sr = m_data_rd;
+ m_bit_cnt = 8;
+ m_byte_ready = true;
+ update_irq();
+ } else {
+ m_data_sr >>= 1;
+ }
+ }
+ return m_wr_bit;
+ } else {
+ return 0;
+ }
+}
+
+void hp2640_tape_device::update_irq()
+{
+ bool new_irq = m_tach_latch || m_hole_latch || m_byte_ready;
+
+ if (new_irq != m_irq) {
+ LOG("IRQ %d\n" , new_irq);
+ m_irq = new_irq;
+ m_irq_handler(m_irq);
+ }
+}
+
+bool hp2640_tape_device::set_speed()
+{
+ hp_dc100_tape_device::tape_speed_t sp;
+
+ if (!BIT(m_cmd_reg , CMD_REG_RUN_BIT)) {
+ sp = hp_dc100_tape_device::SP_STOP;
+ } else if (BIT(m_cmd_reg , CMD_REG_SPEED_BIT)) {
+ sp = hp_dc100_tape_device::SP_FAST;
+ } else {
+ sp = hp_dc100_tape_device::SP_SLOW;
+ }
+
+ bool changed = m_drives[ m_selected_drive ]->set_speed_setpoint(sp , BIT(m_cmd_reg , CMD_REG_DIR_BIT));
+
+ if (changed) {
+ start_rd_wr(true);
+ }
+
+ return changed;
+}
+
+void hp2640_tape_device::start_rd_wr(bool recalc)
+{
+ hp_dc100_tape_device& drive = *m_drives[ m_selected_drive ];
+
+ m_rip = false;
+
+ if (drive.is_above_threshold() && m_current_op == hp_dc100_tape_device::OP_READ) {
+ // Reading
+ LOG("START RD GP %d IS %d\n" , m_gap , m_isf);
+ drive.set_op(hp_dc100_tape_device::OP_READ , recalc);
+ if (m_gap) {
+ m_modulus = MODULUS_RESET;
+ m_bit_sync = false;
+ stop_cell_cnt();
+ }
+ if (!m_isf) {
+ stop_cell_cnt();
+ }
+ } else if (!drive.is_accelerating() && !drive.wpr_r() && m_current_op == hp_dc100_tape_device::OP_WRITE) {
+ // Data writing
+ LOG("START WR\n");
+ stop_cell_cnt();
+ drive.set_op(hp_dc100_tape_device::OP_WRITE);
+ } else if (!drive.wpr_r() && m_current_op == hp_dc100_tape_device::OP_ERASE) {
+ // Gap writing
+ LOG("START ERASE\n");
+ m_rip = true;
+ m_data_sr = 0;
+ m_bit_cnt = 8;
+ stop_cell_cnt();
+ drive.set_op(hp_dc100_tape_device::OP_ERASE);
+ } else {
+ LOG("IDLE\n");
+ stop_cell_cnt();
+ drive.set_op(hp_dc100_tape_device::OP_IDLE);
+ }
+}
+
+void hp2640_tape_device::load_modulus()
+{
+ m_cell_timer->adjust(clocks_to_attotime(256U - m_modulus));
+}
+
+void hp2640_tape_device::restart_cell_cnt()
+{
+ m_cell_cnt = 6;
+ load_modulus();
+}
+
+void hp2640_tape_device::stop_cell_cnt()
+{
+ m_cell_cnt = 6;
+ m_cell_timer->reset();
+}
+
+void hp2640_tape_device::set_gap()
+{
+ LOG("GAP START\n");
+ m_gap = true;
+ m_prev_gap = true;
+ if (m_drives[ m_selected_drive ]->get_op() == hp_dc100_tape_device::OP_READ) {
+ m_modulus = MODULUS_RESET;
+ m_bit_sync = false;
+ stop_cell_cnt();
+ }
+}
+
+void hp2640_tape_device::load_gap_timer()
+{
+ m_gap_timer->adjust(attotime::from_msec(GAP_TIME_MS));
+}
diff --git a/src/mame/machine/hp2640_tape.h b/src/mame/machine/hp2640_tape.h
new file mode 100644
index 00000000000..38733094dd5
--- /dev/null
+++ b/src/mame/machine/hp2640_tape.h
@@ -0,0 +1,96 @@
+// license:BSD-3-Clause
+// copyright-holders: F. Ulivi
+/*********************************************************************
+
+ hp2640_tape.h
+
+ Tape subsystem of HP264x terminals
+
+*********************************************************************/
+
+#ifndef MAME_MACHINE_HP2640_TAPE_H
+#define MAME_MACHINE_HP2640_TAPE_H
+
+#pragma once
+
+#include "machine/hp_dc100_tape.h"
+
+class hp2640_tape_device : public device_t
+{
+public:
+ // construction/destruction
+ hp2640_tape_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock);
+
+ // callbacks
+ auto irq() { return m_irq_handler.bind(); }
+ auto led_0() { return m_led0_handler.bind(); }
+ auto led_1() { return m_led1_handler.bind(); }
+
+ // I/O
+ void command_w(uint8_t cmd);
+ uint8_t status_r ();
+ void data_w (uint8_t data);
+ uint8_t data_r ();
+ uint8_t poll_r () const;
+
+protected:
+ // device-level overrides
+ virtual void device_add_mconfig(machine_config &config) override;
+ virtual void device_start() override;
+ virtual void device_reset() override;
+ virtual void device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr) override;
+
+private:
+ devcb_write_line m_irq_handler;
+ devcb_write_line m_led0_handler;
+ devcb_write_line m_led1_handler;
+
+ // 0 is left hand drive
+ // 1 is right hand drive
+ required_device_array<hp_dc100_tape_device , 2> m_drives;
+
+ // State
+ uint8_t m_selected_drive; // U35-10
+ uint8_t m_cmd_reg; // U35 & others
+ uint8_t m_data_rd; // U32 & U33
+ uint8_t m_data_sr; // U42 & U43
+ uint8_t m_modulus; // U49 & U59
+ uint8_t m_cell_cnt; // U210
+ uint8_t m_bit_cnt; // U310
+ bool m_tach_div2; // U19-6
+ bool m_tach_latch; // U21-10
+ bool m_hole_latch; // U21-6
+ bool m_byte_ready; // U19-9
+ bool m_irq; // U48-8
+ bool m_bit_sync; // U110-6
+ bool m_wr_bit; // U110-10
+ bool m_last_rd_bit;
+ bool m_isf; // U36-6
+ bool m_gap;
+ bool m_prev_gap;
+ bool m_rip;
+ hp_dc100_tape_device::tape_op_t m_current_op;
+
+ // Timers
+ emu_timer *m_gap_timer;
+ emu_timer *m_cell_timer;
+
+ void hole_w(unsigned drive , int state);
+ void tacho_tick_w(unsigned drive , int state);
+ void motion_w(unsigned drive , int state);
+ void rd_bit_w(unsigned drive , int state);
+ int wr_bit_r(unsigned drive);
+ void update_irq();
+ bool set_speed();
+ void start_rd_wr(bool recalc = false);
+ void load_modulus();
+ void restart_cell_cnt();
+ void stop_cell_cnt();
+ void set_gap();
+ void load_gap_timer();
+};
+
+// device type definition
+DECLARE_DEVICE_TYPE(HP2640_TAPE, hp2640_tape_device)
+
+#endif /* MAME_MACHINE_HP2640_TAPE_H */
diff --git a/src/mame/machine/hp9825_tape.cpp b/src/mame/machine/hp9825_tape.cpp
index c3eac66adff..dd85bdfbe09 100644
--- a/src/mame/machine/hp9825_tape.cpp
+++ b/src/mame/machine/hp9825_tape.cpp
@@ -92,7 +92,7 @@ void hp9825_tape_device::device_add_mconfig(machine_config &config)
m_tape->set_acceleration(ACCELERATION);
m_tape->set_set_points(SLOW_SPEED , FAST_SPEED);
m_tape->set_tick_size(TACH_TICK_LENGTH);
- m_tape->set_bits_per_word(17);
+ m_tape->set_image_format(hti_format_t::HTI_DELTA_MOD_17_BITS);
m_tape->set_go_threshold(MOVING_THRESHOLD);
m_tape->cart_out().set(FUNC(hp9825_tape_device::cart_out_w));
m_tape->hole().set(FUNC(hp9825_tape_device::hole_w));
diff --git a/src/tools/imgtool/modules/hp85_tape.cpp b/src/tools/imgtool/modules/hp85_tape.cpp
index fbdef06a7cb..0198b6251dd 100644
--- a/src/tools/imgtool/modules/hp85_tape.cpp
+++ b/src/tools/imgtool/modules/hp85_tape.cpp
@@ -145,7 +145,7 @@ typedef struct {
tape_image_85::tape_image_85(void)
: dirty(false)
{
- image.set_bits_per_word(16);
+ image.set_image_format(hti_format_t::HTI_DELTA_MOD_16_BITS);
}
void tape_image_85::format_img(void)
diff --git a/src/tools/imgtool/modules/hp9845_tape.cpp b/src/tools/imgtool/modules/hp9845_tape.cpp
index e9abc99789c..7a3e90b08d1 100644
--- a/src/tools/imgtool/modules/hp9845_tape.cpp
+++ b/src/tools/imgtool/modules/hp9845_tape.cpp
@@ -324,7 +324,7 @@ static const struct io_procs my_stream_procs = {
imgtoolerr_t tape_image_t::load_from_file(imgtool::stream *stream)
{
hti_format_t inp_image;
- inp_image.set_bits_per_word(16);
+ inp_image.set_image_format(hti_format_t::HTI_DELTA_MOD_16_BITS);
io_generic io;
io.file = (void *)stream;