mirror of
https://github.com/holub/mame
synced 2025-04-16 13:34:55 +03:00
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.
This commit is contained in:
parent
ba587a777c
commit
fad7c9e0be
@ -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",
|
||||
|
@ -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));
|
||||
|
@ -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) {
|
||||
|
@ -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;
|
||||
|
@ -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));
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
};
|
||||
|
||||
|
@ -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));
|
||||
}
|
||||
|
||||
// ************
|
||||
|
@ -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);
|
||||
|
569
src/mame/machine/hp2640_tape.cpp
Normal file
569
src/mame/machine/hp2640_tape.cpp
Normal file
@ -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));
|
||||
}
|
96
src/mame/machine/hp2640_tape.h
Normal file
96
src/mame/machine/hp2640_tape.h
Normal file
@ -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 */
|
@ -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));
|
||||
|
@ -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)
|
||||
|
@ -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;
|
||||
|
Loading…
Reference in New Issue
Block a user