summaryrefslogblamecommitdiffstatshomepage
path: root/src/mame/machine/hp9825_tape.cpp
blob: dbfbd22929e2857a42860704c71311362d30efa0 (plain) (tree)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

















                                                                      
                                        

                                                         
                                                             




















                                                             

                                                                     


                                                                                                           
                                                                                                                                   


                           







                                                                                      



                          






                                                           

  




                                                                                                                       



                               
                                 

                                                


 

                                                                   












                                                                         





                                                                               


                                                                               



                                                                              
                                                                             
 







                                       










                                           



                                         






















                                        








                                  




                                         
 
















































                                                           

                                                     
                                                 





                                                                                  
                                                                                      









                                                                                                                      
                                                           
















                                                           
                                            















































                                                                               





















































































                                                                               








                                     
                                          
 





                                                      
                                         





















                                                                          









                                                   
                                                 
 
                                                     
 

                                                                
                                                          
                

                                                                             
         
 
                                                                               
 
                      
                                  
         



                                                 
                                                                                   
                          

                                                                                                                                   
                               
                                                               

                                                                                                      
                                                               
                
                                                              
         
 
// license:BSD-3-Clause
// copyright-holders:F. Ulivi
/*********************************************************************

    hp9825_tape.cpp

    HP9825 tape sub-system

*********************************************************************/

#include "emu.h"
#include "hp9825_tape.h"
#include "machine/rescap.h"

// Debugging
#include "logmacro.h"
#define LOG_REG_MASK (LOG_GENERAL << 1)
#define LOG_REG(...) LOGMASKED(LOG_REG_MASK, __VA_ARGS__)
#define LOG_DBG_MASK (LOG_REG_MASK << 1)
#define LOG_DBG(...) LOGMASKED(LOG_DBG_MASK, __VA_ARGS__)
#undef VERBOSE
//#define VERBOSE (LOG_GENERAL | LOG_REG_MASK | LOG_DBG_MASK)
#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);
	}
}

// Constants
constexpr double FAST_SPEED = 90.0;             // Fast speed: 90 ips
constexpr double SLOW_SPEED = 22.0;             // Slow speed: 22 ips
constexpr double MOVING_THRESHOLD = 2.0;        // Tape is moving (from MVG bit POV) when speed > 2.0 ips
constexpr double ACCELERATION = 1200.0;         // Acceleration when speed set point is changed: 1200 ips^2
constexpr unsigned TACH_TICKS_PER_INCH = 483;   // Tachometer pulses per inch
constexpr hti_format_t::tape_pos_t TACH_TICK_LENGTH = hti_format_t::ONE_INCH_POS / TACH_TICKS_PER_INCH; // Length of each tach tick

// Bits in command register
enum : unsigned {
	CMD_REG_MOTOR_BIT   = 7,    // Motor on (0)
	CMD_REG_WR_GATE_BIT = 6,    // Write gate (0)
	CMD_REG_SPEED_BIT = 5,      // Tape speed (1 = slow)
	CMD_REG_DIR_BIT = 4,        // Tape direction (1 = fwd)
	CMD_REG_FLG_SEL_BIT = 3,    // FLG selection (0 = tacho pulses, 1 = bit clock)
	CMD_REG_THRESHOLD_BIT = 2,  // Threshold selection
	CMD_REG_DMA_EN_BIT = 1,     // DMA enable (0)
	CMD_REG_TRACK_SEL_BIT = 0   // Track selection (1 = A)
};

// Bits in status register
enum : unsigned {
	STAT_REG_WPR_BIT = 7,   // Write protected (1)
	STAT_REG_DIR_BIT = 6,   // Tape direction (1 = rev)
	STAT_REG_MVG_BIT = 5,   // Tape moving (1)
	STAT_REG_GAP_BIT = 4,   // Gap (1) or data (0)
	STAT_REG_COUT_BIT = 2,  // Cartridge out (1)
	STAT_REG_SVF_BIT = 1,   // Servo failure (1)
	STAT_REG_EOT_BIT = 0    // End of tape (1)
};

// Device type definition
DEFINE_DEVICE_TYPE(HP9825_TAPE, hp9825_tape_device, "hp9825_tape", "HP9825 tape sub-system")

hp9825_tape_device::hp9825_tape_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
: device_t(mconfig , HP9825_TAPE , tag , owner , clock)
	, m_flg_handler(*this)
	, m_sts_handler(*this)
	, m_dmar_handler(*this)
	, m_led_handler(*this)
	, m_tape(*this , "drive")
	, m_short_gap_timer(*this , "short_tmr")
	, m_long_gap_timer(*this , "long_tmr")
{
}

void hp9825_tape_device::device_add_mconfig(machine_config &config)
{
	HP_DC100_TAPE(config , m_tape , 0);
	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_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));
	m_tape->tacho_tick().set(FUNC(hp9825_tape_device::tacho_tick_w));
	m_tape->motion_event().set(FUNC(hp9825_tape_device::motion_w));
	m_tape->rd_bit().set(FUNC(hp9825_tape_device::rd_bit_w));
	m_tape->wr_bit().set(FUNC(hp9825_tape_device::wr_bit_r));

	TTL74123(config , m_short_gap_timer , 0);
	m_short_gap_timer->set_connection_type(TTL74123_NOT_GROUNDED_NO_DIODE);
	m_short_gap_timer->set_resistor_value(RES_K(37.9));
	m_short_gap_timer->set_capacitor_value(CAP_N(10));
	m_short_gap_timer->set_a_pin_value(0);
	m_short_gap_timer->set_clear_pin_value(1);
	m_short_gap_timer->out_cb().set(FUNC(hp9825_tape_device::short_gap_w));

	TTL74123(config , m_long_gap_timer , 0);
	m_long_gap_timer->set_connection_type(TTL74123_NOT_GROUNDED_NO_DIODE);
	m_long_gap_timer->set_resistor_value(RES_K(28.7));
	m_long_gap_timer->set_capacitor_value(CAP_U(0.22));
	m_long_gap_timer->set_clear_pin_value(1);
	m_long_gap_timer->out_cb().set(FUNC(hp9825_tape_device::long_gap_w));
}

void hp9825_tape_device::device_start()
{
	m_flg_handler.resolve_safe();
	m_sts_handler.resolve_safe();
	m_dmar_handler.resolve_safe();
	m_led_handler.resolve_safe();

	save_item(NAME(m_cmd_reg));
	save_item(NAME(m_stat_reg));
	save_item(NAME(m_flg));
	save_item(NAME(m_sts));
	save_item(NAME(m_data_out));
	save_item(NAME(m_data_in));
	save_item(NAME(m_exception));
	save_item(NAME(m_search_complete));
	save_item(NAME(m_dma_req));
	save_item(NAME(m_in_gap));
	save_item(NAME(m_no_go));
	save_item(NAME(m_valid_bits));
	save_item(NAME(m_trans_cnt));
	save_item(NAME(m_short_gap_out));
	save_item(NAME(m_long_gap_out));
}

void hp9825_tape_device::device_reset()
{
	clear_state();
}

void hp9825_tape_device::clear_state()
{
	m_cmd_reg = ~0;
	m_stat_reg = 0;
	// Not actually reset in real hw
	m_flg = false;
	m_sts = true;
	m_data_out = false;
	m_data_in = false;
	m_exception = false;
	m_search_complete = false;
	m_dma_req = false;
	m_in_gap = true;
	m_no_go = false;
	m_valid_bits = false;
	m_trans_cnt = 0;

	m_short_gap_timer->b_w(0);
	m_long_gap_timer->a_w(1);
	m_long_gap_timer->b_w(0);

	m_flg_handler(false);
	m_sts_handler(true);
	m_dmar_handler(false);
	m_led_handler(false);
}

READ16_MEMBER(hp9825_tape_device::tape_r)
{
	uint16_t res = 0;

	switch (offset) {
	case 0:
		// R4: read data out
		if (m_data_out) {
			BIT_SET(res , 0);
		}
		if (!m_no_go) {
			set_flg(false);
		}
		break;

	case 1:
		// R5: read status
		res = m_stat_reg;
		break;

	case 2:
		// R6: clear EOT
		BIT_CLR(m_stat_reg , STAT_REG_EOT_BIT);
		update_sts();
		break;

	default:
		logerror("Reading @ offset %u\n" , offset);
		break;
	}

	LOG_REG("R R%u=%02x\n" , offset + 4 , res);
	return res;
}

WRITE16_MEMBER(hp9825_tape_device::tape_w)
{
	LOG_REG("W R%u=%02x\n" , offset + 4 , data);

	switch (offset) {
	case 0:
		// R4: write data in
		m_data_in = BIT(data , 0);
		m_dma_req = false;
		update_dmar();
		if (!m_no_go) {
			set_flg(false);
		}
		break;

	case 1:
		// R5: write command
		{
			auto old_cmd_reg = m_cmd_reg;
			m_cmd_reg = data;
			check_for_speed_change();
			// Direction bit is mirrored (inverted) in status register
			if (BIT(m_cmd_reg , CMD_REG_DIR_BIT)) {
				BIT_CLR(m_stat_reg , STAT_REG_DIR_BIT);
			} else {
				BIT_SET(m_stat_reg , STAT_REG_DIR_BIT);
			}
			m_tape->set_track_no(!BIT(m_cmd_reg , CMD_REG_TRACK_SEL_BIT));
			if (BIT(m_cmd_reg , CMD_REG_DMA_EN_BIT)) {
				// DMA disabled
				m_search_complete = false;
			}
			update_sts();
			update_dmar();

			if ((old_cmd_reg ^ m_cmd_reg) &
				(BIT_MASK<uint8_t>(CMD_REG_WR_GATE_BIT) | BIT_MASK<uint8_t>(CMD_REG_THRESHOLD_BIT))) {
				// Something changed in Wr gate or threshold bit, start rd/wr
				m_tape->update_speed_pos();
				start_rd_wr();
			}
		}
		break;

	case 2:
		// R6: clear DMA
		if (!BIT(m_cmd_reg , CMD_REG_DMA_EN_BIT)) {
			m_search_complete = true;
			update_sts();
		}
		m_dma_req = false;
		update_dmar();
		break;

	case 3:
		// R7: reset status
		if (!m_tape->cart_out_r()) {
			BIT_CLR(m_stat_reg , STAT_REG_COUT_BIT);
			update_sts();
		}
		break;

	default:
		logerror("Writing @ offset %u\n" , offset);
		break;
	}
}

WRITE_LINE_MEMBER(hp9825_tape_device::short_gap_w)
{
	LOG_DBG("Short gap %d\n" , state);
	m_short_gap_out = state;
	if (!m_short_gap_out) {
		m_valid_bits = false;
		m_data_out = false;
		m_trans_cnt = 0;
		m_in_gap = true;
		m_long_gap_timer->a_w(m_in_gap);
		update_sts();
	}
}

WRITE_LINE_MEMBER(hp9825_tape_device::long_gap_w)
{
	LOG_DBG("Long gap %d\n" , state);
	if (m_long_gap_out && !state && !BIT(m_cmd_reg , CMD_REG_DMA_EN_BIT)) {
		m_dma_req = true;
		update_dmar();
	}
	m_long_gap_out = state;
	if (m_long_gap_out) {
		BIT_CLR(m_stat_reg , STAT_REG_GAP_BIT);
	} else {
		BIT_SET(m_stat_reg , STAT_REG_GAP_BIT);
	}
}

void hp9825_tape_device::set_flg(bool state)
{
	if (state != m_flg) {
		m_flg = state;
		m_flg_handler(m_flg);
	}
}

WRITE_LINE_MEMBER(hp9825_tape_device::cart_out_w)
{
	LOG_DBG("cart_out_w %d\n" , state);
	if (state) {
		// STAT_REG_COUT_BIT is cleared by a write to R7
		BIT_SET(m_stat_reg, STAT_REG_COUT_BIT);
	}

	if (m_tape->wpr_r()) {
		BIT_SET(m_stat_reg, STAT_REG_WPR_BIT);
	} else {
		BIT_CLR(m_stat_reg, STAT_REG_WPR_BIT);
	}

	update_sts();
}

WRITE_LINE_MEMBER(hp9825_tape_device::hole_w)
{
	if (state) {
		LOG_DBG("hole_w\n");
		BIT_SET(m_stat_reg , STAT_REG_EOT_BIT);
		update_sts();
	}
}

WRITE_LINE_MEMBER(hp9825_tape_device::tacho_tick_w)
{
	if (state) {
		LOG_DBG("tacho_tick_w\n");
		if (!BIT(m_cmd_reg , CMD_REG_FLG_SEL_BIT)) {
			set_flg(true);
		}
	}
}

WRITE_LINE_MEMBER(hp9825_tape_device::motion_w)
{
	if (state) {
		LOG_DBG("motion_w\n");
		// Update MVG bit
		if (m_tape->is_moving()) {
			if (!BIT(m_stat_reg , STAT_REG_MVG_BIT)) {
				BIT_SET(m_stat_reg , STAT_REG_MVG_BIT);
				m_led_handler(true);
			}
		} else {
			if (BIT(m_stat_reg , STAT_REG_MVG_BIT)) {
				BIT_CLR(m_stat_reg , STAT_REG_MVG_BIT);
				m_led_handler(false);
			}
		}
		start_rd_wr();
	}
}

WRITE_LINE_MEMBER(hp9825_tape_device::rd_bit_w)
{
	m_short_gap_timer->b_w(1);
	m_short_gap_timer->b_w(0);
	m_long_gap_timer->b_w(1);
	m_long_gap_timer->b_w(0);
	m_data_out = m_valid_bits && state;
	if (BIT(m_cmd_reg , CMD_REG_FLG_SEL_BIT)) {
		set_flg(true);
	}
	m_trans_cnt++;
	LOG_DBG("TC %u IG %d VB %d\n" , m_trans_cnt , m_in_gap , m_valid_bits);
	if ((m_trans_cnt & 0x0c) == 0x0c) {
		m_valid_bits = true;
	}
	if (BIT(m_trans_cnt , 2) && m_in_gap) {
		m_in_gap = false;
		m_long_gap_timer->a_w(m_in_gap);
		update_sts();
	}
}

READ_LINE_MEMBER(hp9825_tape_device::wr_bit_r)
{
	if (BIT(m_cmd_reg , CMD_REG_FLG_SEL_BIT)) {
		set_flg(true);
	}
	return m_data_in;
}

void hp9825_tape_device::update_sts()
{
	// Inputs to STS computation:
	// CMD_REG_MOTOR_BIT
	// CMD_REG_DMA_EN_BIT
	// STAT_REG_EOT_BIT
	// STAT_REG_COUT_BIT
	// m_search_complete
	// m_in_gap
	auto prev_exception = m_exception;

	m_exception =
		BIT(m_stat_reg , STAT_REG_EOT_BIT) ||
		BIT(m_stat_reg , STAT_REG_COUT_BIT) ||
		m_search_complete;

	if (prev_exception != m_exception) {
		check_for_speed_change();
	}

	m_no_go = m_exception && !BIT(m_cmd_reg , CMD_REG_MOTOR_BIT);
	// U6-6
	bool sts_2 = m_in_gap && BIT(m_cmd_reg , CMD_REG_DMA_EN_BIT);

	bool new_sts = m_no_go || sts_2;
	if (new_sts != m_sts) {
		m_sts = new_sts;
		m_sts_handler(m_sts);
	}

	if (m_no_go) {
		set_flg(true);
	}
}

void hp9825_tape_device::update_dmar()
{
	m_dmar_handler(m_dma_req && !BIT(m_cmd_reg , CMD_REG_DMA_EN_BIT));
}

bool hp9825_tape_device::is_moving_fwd() const
{
	return BIT(m_cmd_reg , CMD_REG_DIR_BIT);
}

bool hp9825_tape_device::is_speed_fast() const
{
	return !BIT(m_cmd_reg , CMD_REG_SPEED_BIT);
}

void hp9825_tape_device::check_for_speed_change()
{
	hp_dc100_tape_device::tape_speed_t new_speed;

	if (m_exception || BIT(m_cmd_reg , CMD_REG_MOTOR_BIT)) {
		// Stop
		new_speed = hp_dc100_tape_device::SP_STOP;
	} else {
		new_speed = is_speed_fast() ? hp_dc100_tape_device::SP_FAST :
			hp_dc100_tape_device::SP_SLOW;
	}

	bool changed = m_tape->set_speed_setpoint(new_speed , is_moving_fwd());

	if (changed) {
		start_rd_wr(true);
	}
}

void hp9825_tape_device::start_rd_wr(bool recalc)
{
	if (m_tape->is_above_threshold() && BIT(m_cmd_reg , CMD_REG_WR_GATE_BIT)) {
		// Reading
		m_tape->set_op(hp_dc100_tape_device::OP_READ , recalc);
	} else if (!m_tape->is_accelerating() && !BIT(m_cmd_reg , CMD_REG_WR_GATE_BIT) && BIT(m_cmd_reg , CMD_REG_THRESHOLD_BIT)) {
		// Data writing
		m_tape->set_op(hp_dc100_tape_device::OP_WRITE);
	} else if (!BIT(m_cmd_reg , CMD_REG_WR_GATE_BIT) && !BIT(m_cmd_reg , CMD_REG_THRESHOLD_BIT)) {
		// Gap writing
		m_tape->set_op(hp_dc100_tape_device::OP_ERASE);
	} else {
		m_tape->set_op(hp_dc100_tape_device::OP_IDLE);
	}
}