mirror of
https://github.com/holub/mame
synced 2025-10-05 08:41:31 +03:00
hp85: implemented tape drive
This commit is contained in:
parent
2c317e49e2
commit
4c5ed8b64b
@ -1010,6 +1010,18 @@ if (MACHINES["HP_TACO"]~=null) then
|
||||
}
|
||||
end
|
||||
|
||||
---------------------------------------------------
|
||||
--
|
||||
--@src/devices/machine/1ma6.h,MACHINES["1MA6"] = true
|
||||
---------------------------------------------------
|
||||
|
||||
if (MACHINES["1MA6"]~=null) then
|
||||
files {
|
||||
MAME_DIR .. "src/devices/machine/1ma6.cpp",
|
||||
MAME_DIR .. "src/devices/machine/1ma6.h",
|
||||
}
|
||||
end
|
||||
|
||||
---------------------------------------------------
|
||||
--
|
||||
--@src/devices/machine/i2cmem.h,MACHINES["I2CMEM"] = true
|
||||
|
@ -415,6 +415,7 @@ MACHINES["F3853"] = true
|
||||
MACHINES["HD63450"] = true
|
||||
MACHINES["HD64610"] = true
|
||||
MACHINES["HP_TACO"] = true
|
||||
MACHINES["1MA6"] = true
|
||||
MACHINES["I2CMEM"] = true
|
||||
MACHINES["I7220"] = true
|
||||
MACHINES["I80130"] = true
|
||||
|
702
src/devices/machine/1ma6.cpp
Normal file
702
src/devices/machine/1ma6.cpp
Normal file
@ -0,0 +1,702 @@
|
||||
// license:BSD-3-Clause
|
||||
// copyright-holders:F. Ulivi
|
||||
/*********************************************************************
|
||||
|
||||
1ma6.cpp
|
||||
|
||||
HP-85 tape controller (1MA6-0001)
|
||||
|
||||
*Status of tape tests of service ROM*
|
||||
|
||||
Test Code Description Status
|
||||
==================================
|
||||
I Write protect OK
|
||||
P Status test OK
|
||||
Q Speed test OK
|
||||
R Hole detection Fails with "HOLE D" error (*1)
|
||||
S Write test OK
|
||||
T Read test OK
|
||||
U Record test OK (*2)
|
||||
|
||||
*1 Hole test fails because it depends on the diameter of the
|
||||
holes being correct (this driver doesn't emulate it).
|
||||
*2 Record test is buggy (byte @74ab in service ROM should be 0x54).
|
||||
Test succeeds if this bug is corrected first.
|
||||
|
||||
*********************************************************************/
|
||||
|
||||
#include "1ma6.h"
|
||||
|
||||
// Debugging
|
||||
#define VERBOSE 0
|
||||
#include "logmacro.h"
|
||||
|
||||
// Device type definition
|
||||
DEFINE_DEVICE_TYPE(HP_1MA6, hp_1ma6_device, "hp_1ma6", "HP 1MA6")
|
||||
|
||||
// Bit manipulation
|
||||
namespace {
|
||||
static constexpr unsigned BIT_MASK(unsigned n)
|
||||
{
|
||||
return 1U << n;
|
||||
}
|
||||
|
||||
template<typename T> void BIT_CLR(T& w , unsigned n)
|
||||
{
|
||||
w &= ~(T)BIT_MASK(n);
|
||||
}
|
||||
|
||||
template<typename T> void BIT_SET(T& w , unsigned n)
|
||||
{
|
||||
w |= (T)BIT_MASK(n);
|
||||
}
|
||||
|
||||
template<typename T> void COPY_BIT(bool bit , T& w , unsigned n)
|
||||
{
|
||||
if (bit) {
|
||||
BIT_SET(w , n);
|
||||
} else {
|
||||
BIT_CLR(w , n);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// **** Constants ****
|
||||
// One tachometer tick every 1/32 of inch
|
||||
static constexpr hti_format_t::tape_pos_t TACH_TICK_LEN = hti_format_t::ONE_INCH_POS / 32;
|
||||
static constexpr hti_format_t::tape_pos_t TAPE_INIT_POS = hti_format_t::ONE_INCH_POS * 80;
|
||||
// Slow tape speed (10 ips)
|
||||
static constexpr unsigned TACH_FREQ_SLOW = hti_format_t::ONE_INCH_POS * 10;
|
||||
// Fast tape speed (60 ips)
|
||||
static constexpr unsigned TACH_FREQ_FAST = hti_format_t::ONE_INCH_POS * 60;
|
||||
// Minimum gap size (totally made up)
|
||||
static constexpr hti_format_t::tape_pos_t MIN_GAP_SIZE = TACH_TICK_LEN;
|
||||
// Braking distance at fast speed: 1.5 in (this value assumes the deceleration is 1200 in/s^2, the same as in TACO)
|
||||
static constexpr hti_format_t::tape_pos_t FAST_BRAKE_DIST = hti_format_t::ONE_INCH_POS * 1.5;
|
||||
// Braking distance at slow speed: 1/24 in
|
||||
static constexpr hti_format_t::tape_pos_t SLOW_BRAKE_DIST = hti_format_t::ONE_INCH_POS / 24;
|
||||
|
||||
// Bits in control register
|
||||
static constexpr unsigned CTL_TRACK_NO_BIT = 0; // Track selection
|
||||
static constexpr unsigned CTL_POWER_UP_BIT = 1; // Tape controller power up
|
||||
static constexpr unsigned CTL_MOTOR_ON_BIT = 2; // Motor control
|
||||
static constexpr unsigned CTL_DIR_FWD_BIT = 3; // Tape direction = forward
|
||||
static constexpr unsigned CTL_FAST_BIT = 4; // Speed = fast
|
||||
static constexpr unsigned CTL_WRITE_DATA_BIT = 5; // Write data
|
||||
static constexpr unsigned CTL_WRITE_SYNC_BIT = 6; // Write SYNC
|
||||
static constexpr unsigned CTL_WRITE_GAP_BIT = 7; // Write gap
|
||||
|
||||
// Bits in status register
|
||||
static constexpr unsigned STS_CASSETTE_IN_BIT = 0; // Cassette in
|
||||
static constexpr unsigned STS_STALL_BIT = 1; // Tape stalled
|
||||
static constexpr unsigned STS_ILIM_BIT = 2; // Overcurrent
|
||||
static constexpr unsigned STS_WRITE_EN_BIT = 3; // Write enabled
|
||||
static constexpr unsigned STS_HOLE_BIT = 4; // Hole detected
|
||||
static constexpr unsigned STS_GAP_BIT = 5; // Gap detected
|
||||
static constexpr unsigned STS_TACH_BIT = 6; // Tachometer tick
|
||||
static constexpr unsigned STS_READY_BIT = 7; // Ready
|
||||
|
||||
// Timers
|
||||
enum {
|
||||
TAPE_TMR_ID,
|
||||
HOLE_TMR_ID
|
||||
};
|
||||
|
||||
hp_1ma6_device::hp_1ma6_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
|
||||
: device_t(mconfig , HP_1MA6 , tag , owner , clock),
|
||||
device_image_interface(mconfig , *this),
|
||||
m_image(),
|
||||
m_image_dirty(false)
|
||||
{
|
||||
clear_state();
|
||||
}
|
||||
|
||||
WRITE8_MEMBER(hp_1ma6_device::reg_w)
|
||||
{
|
||||
LOG("WR %u=%02x\n" , offset , data);
|
||||
switch(offset) {
|
||||
case 0:
|
||||
// Control register
|
||||
start_cmd_exec(data);
|
||||
break;
|
||||
|
||||
case 1:
|
||||
// Data register
|
||||
m_data_reg = data;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
READ8_MEMBER(hp_1ma6_device::reg_r)
|
||||
{
|
||||
uint8_t res = 0;
|
||||
|
||||
switch (offset) {
|
||||
case 0:
|
||||
// Status register
|
||||
update_tape_pos();
|
||||
if (m_cmd_state == CMD_IDLE || !m_cartridge_in) {
|
||||
BIT_SET(m_status_reg , STS_READY_BIT);
|
||||
} else if (m_cmd_state == CMD_STOPPING ||
|
||||
m_cmd_state == CMD_FAST_FWD_REV ||
|
||||
m_cmd_state == CMD_WR_GAP) {
|
||||
BIT_CLR(m_status_reg , STS_READY_BIT);
|
||||
}
|
||||
if ((m_control_reg & (BIT_MASK(CTL_WRITE_SYNC_BIT) |
|
||||
BIT_MASK(CTL_WRITE_GAP_BIT))) != 0 &&
|
||||
(!m_cartridge_in || !is_readonly())) {
|
||||
BIT_SET(m_status_reg , STS_WRITE_EN_BIT);
|
||||
} else {
|
||||
BIT_CLR(m_status_reg , STS_WRITE_EN_BIT);
|
||||
}
|
||||
// Gap detection
|
||||
if (m_cmd_state == CMD_IDLE) {
|
||||
BIT_SET(m_status_reg , STS_GAP_BIT);
|
||||
} else if (m_gap_detect_start != hti_format_t::NULL_TAPE_POS) {
|
||||
if (abs(m_gap_detect_start - m_tape_pos) >= MIN_GAP_SIZE) {
|
||||
auto tmp = m_tape_pos;
|
||||
hti_format_t::pos_offset(tmp , is_moving_fwd() , -MIN_GAP_SIZE);
|
||||
if (m_image.just_gap(current_track() , tmp , m_tape_pos)) {
|
||||
BIT_SET(m_status_reg , STS_GAP_BIT);
|
||||
}
|
||||
} else {
|
||||
BIT_SET(m_status_reg , STS_GAP_BIT);
|
||||
}
|
||||
}
|
||||
|
||||
res = m_status_reg;
|
||||
// Clear latching bits
|
||||
BIT_CLR(m_status_reg , STS_HOLE_BIT);
|
||||
BIT_CLR(m_status_reg , STS_GAP_BIT);
|
||||
BIT_CLR(m_status_reg , STS_TACH_BIT);
|
||||
BIT_CLR(m_status_reg , STS_READY_BIT);
|
||||
if (m_cartridge_in) {
|
||||
BIT_SET(m_status_reg , STS_CASSETTE_IN_BIT);
|
||||
}
|
||||
break;
|
||||
|
||||
case 1:
|
||||
// Data register
|
||||
res = m_data_reg;
|
||||
break;
|
||||
}
|
||||
LOG("RD %u=%02x\n" , offset , res);
|
||||
return res;
|
||||
}
|
||||
|
||||
void hp_1ma6_device::device_start()
|
||||
{
|
||||
save_item(NAME(m_data_reg));
|
||||
save_item(NAME(m_status_reg));
|
||||
save_item(NAME(m_control_reg));
|
||||
save_item(NAME(m_tape_pos));
|
||||
save_item(NAME(m_prev_pos));
|
||||
save_item(NAME(m_start_time));
|
||||
save_item(NAME(m_image_dirty));
|
||||
save_item(NAME(m_rw_pos));
|
||||
save_item(NAME(m_next_word));
|
||||
save_item(NAME(m_rd_it_valid));
|
||||
save_item(NAME(m_gap_detect_start));
|
||||
|
||||
m_tape_timer = timer_alloc(TAPE_TMR_ID);
|
||||
m_hole_timer = timer_alloc(HOLE_TMR_ID);
|
||||
}
|
||||
|
||||
void hp_1ma6_device::device_reset()
|
||||
{
|
||||
clear_state();
|
||||
|
||||
m_tape_timer->reset();
|
||||
m_hole_timer->reset();
|
||||
}
|
||||
|
||||
void hp_1ma6_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr)
|
||||
{
|
||||
update_tape_pos();
|
||||
|
||||
switch (id) {
|
||||
case TAPE_TMR_ID:
|
||||
{
|
||||
LOG("TMR %.6f %d %d\n" , machine().time().as_double() , m_tape_pos , m_cmd_state);
|
||||
attotime cmd_duration = attotime::never;
|
||||
switch (m_cmd_state) {
|
||||
case CMD_STOPPING:
|
||||
m_cmd_state = CMD_IDLE;
|
||||
stop_tape();
|
||||
break;
|
||||
|
||||
case CMD_RD_WAIT_SYNC:
|
||||
m_tape_pos = m_rw_pos;
|
||||
LOG("RD WS @ %d = %04x\n" , m_tape_pos , m_rd_it->second);
|
||||
if (m_rd_it->second != 0xffff) {
|
||||
// Sync achieved
|
||||
m_cmd_state = CMD_RD_MSB;
|
||||
}
|
||||
cmd_duration = advance_rd_state();
|
||||
break;
|
||||
|
||||
case CMD_RD_MSB:
|
||||
m_tape_pos = m_rw_pos;
|
||||
m_next_word = m_rd_it->second;
|
||||
LOG("RD T%u @ %d = %04x\n" , current_track() , m_rw_pos , m_next_word);
|
||||
m_data_reg = (uint8_t)(m_next_word >> 8);
|
||||
BIT_SET(m_status_reg , STS_READY_BIT);
|
||||
// Time to reach LSB: half the length of a 0 word
|
||||
// Actually it varies basing on MSB content
|
||||
cmd_duration = time_to_distance(hti_format_t::word_length(0) / 2);
|
||||
m_cmd_state = CMD_RD_LSB;
|
||||
break;
|
||||
|
||||
case CMD_RD_LSB:
|
||||
m_data_reg = (uint8_t)m_next_word;
|
||||
BIT_SET(m_status_reg , STS_READY_BIT);
|
||||
m_cmd_state = CMD_RD_MSB;
|
||||
cmd_duration = advance_rd_state();
|
||||
break;
|
||||
|
||||
case CMD_WR_SYNC:
|
||||
m_tape_pos = m_rw_pos;
|
||||
if (m_rd_it->second != 0xffff) {
|
||||
// Got preamble
|
||||
m_next_word = (hti_format_t::tape_word_t)m_data_reg << 8;
|
||||
BIT_SET(m_status_reg , STS_READY_BIT);
|
||||
m_cmd_state = CMD_WR_LSB;
|
||||
cmd_duration = time_to_distance(hti_format_t::word_length(0) / 2);
|
||||
} else {
|
||||
cmd_duration = advance_rd_state();
|
||||
}
|
||||
break;
|
||||
|
||||
case CMD_WR_MSB:
|
||||
// **** FWD ****
|
||||
// | m_rw_pos --> m_next_word
|
||||
// | m_tape_pos --> (start of next word)
|
||||
// V
|
||||
//
|
||||
// **** REV ****
|
||||
// ^ m_tape_pos --> m_next_word
|
||||
// | m_rw_pos --> (start of previous word)
|
||||
// |
|
||||
{
|
||||
auto length = hti_format_t::word_length(m_next_word);
|
||||
if (!is_moving_fwd()) {
|
||||
hti_format_t::pos_offset(m_rw_pos , false , length);
|
||||
}
|
||||
LOG("WR T%u @ %d = %04x\n" , current_track() , m_rw_pos , m_next_word);
|
||||
if (m_cartridge_in) {
|
||||
m_image.write_word(current_track() , m_rw_pos , m_next_word , length);
|
||||
m_image_dirty = true;
|
||||
}
|
||||
if (is_moving_fwd()) {
|
||||
hti_format_t::pos_offset(m_rw_pos , true , length);
|
||||
}
|
||||
m_tape_pos = m_rw_pos;
|
||||
|
||||
m_next_word = (hti_format_t::tape_word_t)m_data_reg << 8;
|
||||
BIT_SET(m_status_reg , STS_READY_BIT);
|
||||
m_cmd_state = CMD_WR_LSB;
|
||||
cmd_duration = time_to_distance(hti_format_t::word_length(0) / 2);
|
||||
}
|
||||
break;
|
||||
|
||||
case CMD_WR_LSB:
|
||||
{
|
||||
m_next_word = (m_next_word & 0xff00) | m_data_reg;
|
||||
BIT_SET(m_status_reg , STS_READY_BIT);
|
||||
m_cmd_state = CMD_WR_MSB;
|
||||
auto length = hti_format_t::word_length(m_next_word);
|
||||
auto tmp = m_rw_pos;
|
||||
hti_format_t::pos_offset(tmp , is_moving_fwd() , length);
|
||||
cmd_duration = time_to_target(tmp);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
m_tape_timer->adjust(cmd_duration);
|
||||
}
|
||||
break;
|
||||
|
||||
case HOLE_TMR_ID:
|
||||
BIT_SET(m_status_reg , STS_HOLE_BIT);
|
||||
m_hole_timer->adjust(time_to_next_hole());
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
image_init_result hp_1ma6_device::call_load()
|
||||
{
|
||||
LOG("call_load\n");
|
||||
return internal_load(false);
|
||||
}
|
||||
|
||||
image_init_result hp_1ma6_device::call_create(int format_type, util::option_resolution *format_options)
|
||||
{
|
||||
LOG("call_create\n");
|
||||
return internal_load(true);
|
||||
}
|
||||
|
||||
void hp_1ma6_device::call_unload()
|
||||
{
|
||||
LOG("call_unload dirty=%d\n" , m_image_dirty);
|
||||
|
||||
device_reset();
|
||||
|
||||
if (m_image_dirty) {
|
||||
io_generic io;
|
||||
io.file = (device_image_interface *)this;
|
||||
io.procs = &image_ioprocs;
|
||||
io.filler = 0;
|
||||
m_image.save_tape(&io);
|
||||
m_image_dirty = false;
|
||||
}
|
||||
|
||||
m_image.clear_tape();
|
||||
set_cartridge_in(false);
|
||||
}
|
||||
|
||||
std::string hp_1ma6_device::call_display()
|
||||
{
|
||||
std::string buffer;
|
||||
// Mostly lifted from cassette_image_device::call_display ;)
|
||||
// See also hp_taco_device::call_display
|
||||
|
||||
// Do not show anything if image not loaded or tape not moving
|
||||
if (!exists() || m_start_time.is_never()) {
|
||||
return buffer;
|
||||
}
|
||||
|
||||
char track = BIT(m_control_reg , CTL_TRACK_NO_BIT) ? 'B' : 'A';
|
||||
char r_w = m_cmd_state == CMD_WR_SYNC ||
|
||||
m_cmd_state == CMD_WR_MSB ||
|
||||
m_cmd_state == CMD_WR_LSB ||
|
||||
m_cmd_state == CMD_WR_GAP ? 'W' : 'R';
|
||||
char m1;
|
||||
char m2;
|
||||
|
||||
if (is_moving_fwd()) {
|
||||
m1 = '>';
|
||||
m2 = BIT(m_control_reg , CTL_FAST_BIT) ? '>' : ' ';
|
||||
} else {
|
||||
m1 = '<';
|
||||
m2 = BIT(m_control_reg , CTL_FAST_BIT) ? '<' : ' ';
|
||||
}
|
||||
|
||||
int pos_in = current_tape_pos() / hti_format_t::ONE_INCH_POS;
|
||||
|
||||
buffer = string_format("%c %c %c%c [%04d/1824]" , track , r_w , m1 , m2 , pos_in);
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
const char *hp_1ma6_device::file_extensions() const
|
||||
{
|
||||
return "hti";
|
||||
}
|
||||
|
||||
void hp_1ma6_device::clear_state()
|
||||
{
|
||||
m_data_reg = 0;
|
||||
m_status_reg = 0;
|
||||
m_control_reg = 0;
|
||||
m_tape_pos = TAPE_INIT_POS;
|
||||
m_prev_pos = TAPE_INIT_POS;
|
||||
m_start_time = attotime::never;
|
||||
m_rw_pos = hti_format_t::NULL_TAPE_POS;
|
||||
m_next_word = 0;
|
||||
m_rd_it_valid = false;
|
||||
m_gap_detect_start = hti_format_t::NULL_TAPE_POS;
|
||||
m_cmd_state = CMD_IDLE;
|
||||
set_cartridge_in(false);
|
||||
set_cartridge_in(is_loaded());
|
||||
}
|
||||
|
||||
unsigned hp_1ma6_device::current_track() const
|
||||
{
|
||||
return BIT(m_control_reg , CTL_TRACK_NO_BIT);
|
||||
}
|
||||
|
||||
unsigned hp_1ma6_device::speed_to_tick_freq(void) const
|
||||
{
|
||||
unsigned freq = BIT(m_control_reg , CTL_FAST_BIT) ? TACH_FREQ_FAST : TACH_FREQ_SLOW;
|
||||
|
||||
if (m_cmd_state == CMD_STOPPING) {
|
||||
// Braking
|
||||
freq /= 2;
|
||||
}
|
||||
|
||||
return freq;
|
||||
}
|
||||
|
||||
attotime hp_1ma6_device::time_to_distance(hti_format_t::tape_pos_t distance) const
|
||||
{
|
||||
// +1 for rounding
|
||||
return attotime::from_ticks(distance + 1 , speed_to_tick_freq());
|
||||
}
|
||||
|
||||
attotime hp_1ma6_device::time_to_target(hti_format_t::tape_pos_t target) const
|
||||
{
|
||||
return time_to_distance(abs(target - m_tape_pos));
|
||||
}
|
||||
|
||||
attotime hp_1ma6_device::time_to_next_hole(void) const
|
||||
{
|
||||
auto pos = hti_format_t::next_hole(m_tape_pos , is_moving_fwd());
|
||||
|
||||
if (pos == hti_format_t::NULL_TAPE_POS) {
|
||||
return attotime::never;
|
||||
} else {
|
||||
return time_to_target(pos);
|
||||
}
|
||||
}
|
||||
|
||||
attotime hp_1ma6_device::time_to_rd_next_word(hti_format_t::tape_pos_t& word_rd_pos) const
|
||||
{
|
||||
if (m_rd_it_valid) {
|
||||
word_rd_pos = hti_format_t::farthest_end(m_rd_it , is_moving_fwd());
|
||||
return time_to_target(word_rd_pos);
|
||||
} else {
|
||||
return attotime::never;
|
||||
}
|
||||
}
|
||||
|
||||
hti_format_t::tape_pos_t hp_1ma6_device::current_tape_pos() const
|
||||
{
|
||||
if (m_start_time.is_never()) {
|
||||
// Tape not moving
|
||||
return m_tape_pos;
|
||||
}
|
||||
|
||||
attotime delta_time(machine().time() - m_start_time);
|
||||
hti_format_t::tape_pos_t delta_tach = (hti_format_t::tape_pos_t)(delta_time.as_ticks(speed_to_tick_freq()));
|
||||
|
||||
auto tape_pos = m_tape_pos;
|
||||
if (!hti_format_t::pos_offset(tape_pos , is_moving_fwd() , delta_tach)) {
|
||||
LOG("Tape unspooled!\n");
|
||||
}
|
||||
|
||||
return tape_pos;
|
||||
}
|
||||
|
||||
void hp_1ma6_device::update_tape_pos()
|
||||
{
|
||||
if (m_start_time.is_never()) {
|
||||
// Tape not moving
|
||||
return;
|
||||
}
|
||||
|
||||
m_tape_pos = current_tape_pos();
|
||||
m_start_time = machine().time();
|
||||
|
||||
// Tachometer ticks
|
||||
if ((m_prev_pos / TACH_TICK_LEN) != (m_tape_pos / TACH_TICK_LEN)) {
|
||||
BIT_SET(m_status_reg , STS_TACH_BIT);
|
||||
}
|
||||
m_prev_pos = m_tape_pos;
|
||||
}
|
||||
|
||||
bool hp_1ma6_device::is_reading() const
|
||||
{
|
||||
return m_cmd_state >= CMD_RD_WAIT_SYNC &&
|
||||
m_cmd_state <= CMD_RD_LSB;
|
||||
}
|
||||
|
||||
bool hp_1ma6_device::is_writing() const
|
||||
{
|
||||
return m_cmd_state >= CMD_WR_SYNC &&
|
||||
m_cmd_state <= CMD_WR_GAP;
|
||||
}
|
||||
|
||||
bool hp_1ma6_device::is_moving_fwd() const
|
||||
{
|
||||
return BIT(m_control_reg , CTL_DIR_FWD_BIT);
|
||||
}
|
||||
|
||||
void hp_1ma6_device::set_cartridge_in(bool in)
|
||||
{
|
||||
m_cartridge_in = in;
|
||||
if (!m_cartridge_in) {
|
||||
BIT_CLR(m_status_reg , STS_CASSETTE_IN_BIT);
|
||||
}
|
||||
}
|
||||
|
||||
void hp_1ma6_device::start_tape()
|
||||
{
|
||||
m_start_time = machine().time();
|
||||
m_prev_pos = m_tape_pos;
|
||||
if (!is_writing()) {
|
||||
m_gap_detect_start = m_tape_pos;
|
||||
} else {
|
||||
m_gap_detect_start = hti_format_t::NULL_TAPE_POS;
|
||||
}
|
||||
m_hole_timer->adjust(time_to_next_hole());
|
||||
}
|
||||
|
||||
void hp_1ma6_device::stop_tape()
|
||||
{
|
||||
m_start_time = attotime::never;
|
||||
m_gap_detect_start = hti_format_t::NULL_TAPE_POS;
|
||||
m_hole_timer->reset();
|
||||
m_tape_timer->reset();
|
||||
}
|
||||
|
||||
attotime hp_1ma6_device::advance_rd_state()
|
||||
{
|
||||
auto res = m_image.adv_it(current_track() , is_moving_fwd() , m_rd_it);
|
||||
if (res == hti_format_t::ADV_NO_MORE_DATA) {
|
||||
m_rd_it_valid = false;
|
||||
} else if (res == hti_format_t::ADV_DISCONT_DATA && is_reading()) {
|
||||
// Discontinuous data: move back to sync search (probably..)
|
||||
m_cmd_state = CMD_RD_WAIT_SYNC;
|
||||
}
|
||||
return time_to_rd_next_word(m_rw_pos);
|
||||
}
|
||||
|
||||
void hp_1ma6_device::start_cmd_exec(uint8_t new_ctl_reg)
|
||||
{
|
||||
if (new_ctl_reg == m_control_reg) {
|
||||
return;
|
||||
}
|
||||
|
||||
cmd_state_t new_state;
|
||||
|
||||
if (!BIT(new_ctl_reg , CTL_POWER_UP_BIT)) {
|
||||
new_state = CMD_IDLE;
|
||||
} else if (!BIT(new_ctl_reg , CTL_MOTOR_ON_BIT)) {
|
||||
new_state = CMD_STOPPING;
|
||||
} else switch (new_ctl_reg & (BIT_MASK(CTL_FAST_BIT) |
|
||||
BIT_MASK(CTL_WRITE_DATA_BIT) |
|
||||
BIT_MASK(CTL_WRITE_SYNC_BIT) |
|
||||
BIT_MASK(CTL_WRITE_GAP_BIT))) {
|
||||
case 0:
|
||||
new_state = CMD_RD_WAIT_SYNC;
|
||||
break;
|
||||
|
||||
case BIT_MASK(CTL_FAST_BIT):
|
||||
case BIT_MASK(CTL_WRITE_GAP_BIT):
|
||||
case BIT_MASK(CTL_WRITE_GAP_BIT) | BIT_MASK(CTL_WRITE_DATA_BIT):
|
||||
new_state = CMD_FAST_FWD_REV;
|
||||
break;
|
||||
|
||||
case BIT_MASK(CTL_WRITE_DATA_BIT):
|
||||
new_state = CMD_WR_SYNC;
|
||||
break;
|
||||
|
||||
case BIT_MASK(CTL_WRITE_SYNC_BIT):
|
||||
new_state = CMD_WR_MSB;
|
||||
break;
|
||||
|
||||
case BIT_MASK(CTL_WRITE_SYNC_BIT) | BIT_MASK(CTL_WRITE_GAP_BIT):
|
||||
new_state = CMD_WR_GAP;
|
||||
break;
|
||||
|
||||
default:
|
||||
LOG("Unknown command %02x\n" , new_ctl_reg);
|
||||
return;
|
||||
}
|
||||
|
||||
update_tape_pos();
|
||||
LOG("State %d -> %d pos = %d\n" , m_cmd_state , new_state , m_tape_pos);
|
||||
|
||||
if (m_cmd_state == CMD_WR_GAP) {
|
||||
// Finish gap writing
|
||||
LOG("T%u: Gap from %d to %d\n" , current_track() , m_rw_pos , m_tape_pos);
|
||||
if (m_cartridge_in) {
|
||||
m_image.write_gap(current_track() , m_rw_pos , m_tape_pos);
|
||||
m_image_dirty = true;
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t saved_dir_speed = m_control_reg & (BIT_MASK(CTL_DIR_FWD_BIT) | BIT_MASK(CTL_FAST_BIT));
|
||||
|
||||
m_control_reg = new_ctl_reg;
|
||||
|
||||
attotime cmd_duration = attotime::never;
|
||||
|
||||
switch (new_state) {
|
||||
case CMD_IDLE:
|
||||
m_cmd_state = CMD_IDLE;
|
||||
stop_tape();
|
||||
break;
|
||||
|
||||
case CMD_STOPPING:
|
||||
if (m_cmd_state != CMD_IDLE &&
|
||||
m_cmd_state != CMD_STOPPING) {
|
||||
m_cmd_state = CMD_STOPPING;
|
||||
// Keep speed & direction from before the STOP command
|
||||
m_control_reg = (m_control_reg & ~(BIT_MASK(CTL_DIR_FWD_BIT) | BIT_MASK(CTL_FAST_BIT))) | saved_dir_speed;
|
||||
cmd_duration = time_to_distance(BIT(m_control_reg , CTL_FAST_BIT) ? FAST_BRAKE_DIST : SLOW_BRAKE_DIST);
|
||||
}
|
||||
break;
|
||||
|
||||
case CMD_RD_WAIT_SYNC:
|
||||
m_cmd_state = CMD_RD_WAIT_SYNC;
|
||||
start_tape();
|
||||
m_rd_it_valid = m_image.next_data(current_track() , m_tape_pos , is_moving_fwd() , false , m_rd_it);
|
||||
cmd_duration = time_to_rd_next_word(m_rw_pos);
|
||||
// TODO: ??
|
||||
BIT_CLR(m_status_reg , STS_READY_BIT);
|
||||
break;
|
||||
|
||||
case CMD_WR_SYNC:
|
||||
m_cmd_state = CMD_WR_SYNC;
|
||||
start_tape();
|
||||
m_rd_it_valid = m_image.next_data(current_track() , m_tape_pos , is_moving_fwd() , false , m_rd_it);
|
||||
cmd_duration = time_to_rd_next_word(m_rw_pos);
|
||||
BIT_SET(m_status_reg , STS_READY_BIT);
|
||||
break;
|
||||
|
||||
case CMD_WR_MSB:
|
||||
m_cmd_state = CMD_WR_MSB;
|
||||
start_tape();
|
||||
m_rw_pos = m_tape_pos;
|
||||
// SYNC word: MSB = 00, LSB = data register
|
||||
m_next_word = (hti_format_t::tape_word_t)m_data_reg;
|
||||
BIT_SET(m_status_reg , STS_READY_BIT);
|
||||
cmd_duration = time_to_distance(hti_format_t::word_length(m_next_word));
|
||||
break;
|
||||
|
||||
case CMD_WR_GAP:
|
||||
m_cmd_state = CMD_WR_GAP;
|
||||
start_tape();
|
||||
m_rw_pos = m_tape_pos;
|
||||
break;
|
||||
|
||||
case CMD_FAST_FWD_REV:
|
||||
m_cmd_state = CMD_FAST_FWD_REV;
|
||||
start_tape();
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
m_tape_timer->adjust(cmd_duration);
|
||||
}
|
||||
|
||||
image_init_result hp_1ma6_device::internal_load(bool is_create)
|
||||
{
|
||||
device_reset();
|
||||
|
||||
io_generic io;
|
||||
io.file = (device_image_interface *)this;
|
||||
io.procs = &image_ioprocs;
|
||||
io.filler = 0;
|
||||
if (is_create) {
|
||||
m_image.clear_tape();
|
||||
m_image.save_tape(&io);
|
||||
} else if (!m_image.load_tape(&io)) {
|
||||
seterror(IMAGE_ERROR_INVALIDIMAGE , "Wrong format");
|
||||
set_cartridge_in(false);
|
||||
return image_init_result::FAIL;
|
||||
}
|
||||
|
||||
m_image_dirty = false;
|
||||
set_cartridge_in(true);
|
||||
|
||||
return image_init_result::PASS;
|
||||
}
|
117
src/devices/machine/1ma6.h
Normal file
117
src/devices/machine/1ma6.h
Normal file
@ -0,0 +1,117 @@
|
||||
// license:BSD-3-Clause
|
||||
// copyright-holders:F. Ulivi
|
||||
/*********************************************************************
|
||||
|
||||
1ma6.h
|
||||
|
||||
HP-85 tape controller (1MA6-0001)
|
||||
|
||||
*********************************************************************/
|
||||
|
||||
#ifndef MAME_MACHINE_1MA6_H
|
||||
#define MAME_MACHINE_1MA6_H
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "formats/hti_tape.h"
|
||||
|
||||
class hp_1ma6_device : public device_t ,
|
||||
public device_image_interface
|
||||
{
|
||||
public:
|
||||
// construction/destruction
|
||||
hp_1ma6_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock);
|
||||
|
||||
// Register read/write
|
||||
DECLARE_WRITE8_MEMBER(reg_w);
|
||||
DECLARE_READ8_MEMBER(reg_r);
|
||||
|
||||
protected:
|
||||
// device-level overrides
|
||||
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;
|
||||
|
||||
// device_image_interface overrides
|
||||
virtual image_init_result call_load() override;
|
||||
virtual image_init_result call_create(int format_type, util::option_resolution *format_options) override;
|
||||
virtual void call_unload() override;
|
||||
virtual std::string call_display() override;
|
||||
virtual iodevice_t image_type() const override { return IO_MAGTAPE; }
|
||||
virtual bool is_readable() const override { return true; }
|
||||
virtual bool is_writeable() const override { return true; }
|
||||
virtual bool is_creatable() const override { return true; }
|
||||
virtual bool must_be_loaded() const override { return false; }
|
||||
virtual bool is_reset_on_load() const override { return false; }
|
||||
virtual const char *file_extensions() const override;
|
||||
|
||||
private:
|
||||
// Registers
|
||||
uint8_t m_data_reg;
|
||||
uint8_t m_status_reg;
|
||||
uint8_t m_control_reg;
|
||||
|
||||
// Tape position & motion
|
||||
hti_format_t::tape_pos_t m_tape_pos;
|
||||
hti_format_t::tape_pos_t m_prev_pos;
|
||||
attotime m_start_time; // Tape moving if != never
|
||||
|
||||
// Timers
|
||||
emu_timer *m_tape_timer;
|
||||
emu_timer *m_hole_timer;
|
||||
|
||||
// Content of tape
|
||||
hti_format_t m_image;
|
||||
bool m_image_dirty;
|
||||
|
||||
// Reading & writing
|
||||
hti_format_t::tape_pos_t m_rw_pos;
|
||||
hti_format_t::tape_word_t m_next_word;
|
||||
hti_format_t::track_iterator_t m_rd_it;
|
||||
bool m_rd_it_valid;
|
||||
|
||||
// Gap detection
|
||||
hti_format_t::tape_pos_t m_gap_detect_start;
|
||||
|
||||
// Command FSM state
|
||||
typedef enum {
|
||||
CMD_IDLE,
|
||||
CMD_STOPPING,
|
||||
CMD_RD_WAIT_SYNC,
|
||||
CMD_RD_MSB,
|
||||
CMD_RD_LSB,
|
||||
CMD_WR_SYNC,
|
||||
CMD_WR_MSB,
|
||||
CMD_WR_LSB,
|
||||
CMD_WR_GAP,
|
||||
CMD_FAST_FWD_REV
|
||||
} cmd_state_t;
|
||||
cmd_state_t m_cmd_state;
|
||||
|
||||
// Cartridge-in status
|
||||
bool m_cartridge_in;
|
||||
|
||||
void clear_state();
|
||||
unsigned current_track() const;
|
||||
unsigned speed_to_tick_freq(void) const;
|
||||
attotime time_to_distance(hti_format_t::tape_pos_t distance) const;
|
||||
attotime time_to_target(hti_format_t::tape_pos_t target) const;
|
||||
attotime time_to_next_hole(void) const;
|
||||
attotime time_to_rd_next_word(hti_format_t::tape_pos_t& word_rd_pos) const;
|
||||
hti_format_t::tape_pos_t current_tape_pos() const;
|
||||
void update_tape_pos();
|
||||
bool is_reading() const;
|
||||
bool is_writing() const;
|
||||
bool is_moving_fwd() const;
|
||||
void set_cartridge_in(bool in);
|
||||
void start_tape();
|
||||
void stop_tape();
|
||||
attotime advance_rd_state();
|
||||
void start_cmd_exec(uint8_t new_ctl_reg);
|
||||
image_init_result internal_load(bool is_create);
|
||||
};
|
||||
|
||||
// device type definition
|
||||
DECLARE_DEVICE_TYPE(HP_1MA6, hp_1ma6_device)
|
||||
|
||||
#endif /* MAME_MACHINE_1MA6_H */
|
@ -14,6 +14,7 @@
|
||||
#include "sound/beep.h"
|
||||
#include "sound/dac.h"
|
||||
#include "sound/volt_reg.h"
|
||||
#include "machine/1ma6.h"
|
||||
|
||||
// Debugging
|
||||
#define VERBOSE 1
|
||||
@ -1017,6 +1018,7 @@ static ADDRESS_MAP_START(cpu_mem_map , AS_PROGRAM , 8 , hp85_state)
|
||||
AM_RANGE(0xff02 , 0xff02) AM_READWRITE(keysts_r , keysts_w)
|
||||
AM_RANGE(0xff03 , 0xff03) AM_READWRITE(keycod_r , keycod_w)
|
||||
AM_RANGE(0xff04 , 0xff07) AM_READWRITE(crtc_r , crtc_w)
|
||||
AM_RANGE(0xff08 , 0xff09) AM_DEVREADWRITE("tape" , hp_1ma6_device , reg_r , reg_w)
|
||||
AM_RANGE(0xff0a , 0xff0a) AM_READWRITE(clksts_r , clksts_w)
|
||||
AM_RANGE(0xff0b , 0xff0b) AM_READWRITE(clkdat_r , clkdat_w)
|
||||
AM_RANGE(0xff18 , 0xff18) AM_WRITE(rselec_w)
|
||||
@ -1051,6 +1053,8 @@ static MACHINE_CONFIG_START(hp85)
|
||||
MCFG_SOUND_ADD("beeper" , BEEP , MASTER_CLOCK / 8192)
|
||||
MCFG_MIXER_ROUTE(ALL_OUTPUTS , "mono" , 0.5 , 0)
|
||||
|
||||
// Tape drive
|
||||
MCFG_DEVICE_ADD("tape" , HP_1MA6 , 0)
|
||||
MACHINE_CONFIG_END
|
||||
|
||||
ROM_START(hp85)
|
||||
|
Loading…
Reference in New Issue
Block a user