diff options
author | 2017-08-16 10:37:39 +0200 | |
---|---|---|
committer | 2017-08-16 10:38:22 +0200 | |
commit | 4c5ed8b64b6ce10de022a151b9d647c3b25fcab1 (patch) | |
tree | 11b11a6551835d9a83ac20e5321af5ff00f44a70 | |
parent | 2c317e49e23e9fb9bc0214c6eee1156cee8a8c87 (diff) |
hp85: implemented tape drive
-rw-r--r-- | scripts/src/machine.lua | 12 | ||||
-rw-r--r-- | scripts/target/mame/mess.lua | 1 | ||||
-rw-r--r-- | src/devices/machine/1ma6.cpp | 702 | ||||
-rw-r--r-- | src/devices/machine/1ma6.h | 117 | ||||
-rw-r--r-- | src/mame/drivers/hp80.cpp | 4 |
5 files changed, 836 insertions, 0 deletions
diff --git a/scripts/src/machine.lua b/scripts/src/machine.lua index 0683ab0a0b9..277d6ba95e5 100644 --- a/scripts/src/machine.lua +++ b/scripts/src/machine.lua @@ -1012,6 +1012,18 @@ 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 --------------------------------------------------- diff --git a/scripts/target/mame/mess.lua b/scripts/target/mame/mess.lua index 33a1cf081d6..eb8a51930a3 100644 --- a/scripts/target/mame/mess.lua +++ b/scripts/target/mame/mess.lua @@ -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 diff --git a/src/devices/machine/1ma6.cpp b/src/devices/machine/1ma6.cpp new file mode 100644 index 00000000000..3f2fa2f942a --- /dev/null +++ b/src/devices/machine/1ma6.cpp @@ -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; +} diff --git a/src/devices/machine/1ma6.h b/src/devices/machine/1ma6.h new file mode 100644 index 00000000000..2a227c496c6 --- /dev/null +++ b/src/devices/machine/1ma6.h @@ -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 */ diff --git a/src/mame/drivers/hp80.cpp b/src/mame/drivers/hp80.cpp index 1a2495c9f84..5438720d12f 100644 --- a/src/mame/drivers/hp80.cpp +++ b/src/mame/drivers/hp80.cpp @@ -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) |