mirror of
https://github.com/holub/mame
synced 2025-04-23 00:39:36 +03:00
HP9845: TACO driver re-written (#5601)
* hp9825: fixed a bug in 9825t * hp9845: TACO driver re-written from scratch, DC100 tape separated into a new device, various adaptations * hp9845: "new TACO" renamed to just "TACO"
This commit is contained in:
parent
eaae4640aa
commit
d4e2fbd306
@ -1190,6 +1190,18 @@ if (MACHINES["HD64610"]~=null) then
|
||||
}
|
||||
end
|
||||
|
||||
---------------------------------------------------
|
||||
--
|
||||
--@src/devices/machine/hp_dc100_tape.h,MACHINES["HP_DC100_TAPE"] = true
|
||||
---------------------------------------------------
|
||||
|
||||
if (MACHINES["HP_DC100_TAPE"]~=null) then
|
||||
files {
|
||||
MAME_DIR .. "src/devices/machine/hp_dc100_tape.cpp",
|
||||
MAME_DIR .. "src/devices/machine/hp_dc100_tape.h",
|
||||
}
|
||||
end
|
||||
|
||||
---------------------------------------------------
|
||||
--
|
||||
--@src/devices/machine/hp_taco.h,MACHINES["HP_TACO"] = true
|
||||
|
@ -471,6 +471,7 @@ MACHINES["ER2055"] = true
|
||||
MACHINES["F3853"] = true
|
||||
MACHINES["HD63450"] = true
|
||||
MACHINES["HD64610"] = true
|
||||
MACHINES["HP_DC100_TAPE"] = true
|
||||
MACHINES["HP_TACO"] = true
|
||||
MACHINES["1MA6"] = true
|
||||
MACHINES["1MB5"] = true
|
||||
|
@ -404,9 +404,9 @@ bool hp_hybrid_cpu_device::execute_one_bpc(uint16_t opcode , uint16_t& next_pc)
|
||||
case 0x4000:
|
||||
// JSM
|
||||
m_icount -= 5;
|
||||
next_pc = remove_mae(get_ea(opcode));
|
||||
m_reg_R = (m_reg_R + 1) & m_addr_mask_low16;
|
||||
WM(AEC_CASE_C , m_reg_R , m_reg_P);
|
||||
next_pc = remove_mae(get_ea(opcode));
|
||||
return true;
|
||||
|
||||
case 0x4800:
|
||||
|
@ -29,8 +29,14 @@
|
||||
#include "1ma6.h"
|
||||
|
||||
// Debugging
|
||||
#define VERBOSE 0
|
||||
#include "logmacro.h"
|
||||
#define LOG_DBG_MASK (LOG_GENERAL << 1)
|
||||
#define LOG_DBG(...) LOGMASKED(LOG_DBG_MASK, __VA_ARGS__)
|
||||
#define LOG_RW_MASK (LOG_DBG_MASK << 1)
|
||||
#define LOG_RW(...) LOGMASKED(LOG_RW_MASK, __VA_ARGS__)
|
||||
#undef VERBOSE
|
||||
//#define VERBOSE (LOG_GENERAL | LOG_DBG_MASK | LOG_RW_MASK)
|
||||
#define VERBOSE 0
|
||||
|
||||
// Device type definition
|
||||
DEFINE_DEVICE_TYPE(HP_1MA6, hp_1ma6_device, "hp_1ma6", "HP 1MA6")
|
||||
@ -63,19 +69,14 @@ namespace {
|
||||
}
|
||||
|
||||
// **** 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 = 2.0; // Tape is moving when speed > 2.0 ips
|
||||
constexpr double ACCELERATION = 1200.0; // Acceleration when speed set point is changed: 1200 ips^2
|
||||
// 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;
|
||||
constexpr hti_format_t::tape_pos_t TACH_TICK_LEN = hti_format_t::ONE_INCH_POS / 32;
|
||||
// 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;
|
||||
constexpr hti_format_t::tape_pos_t MIN_GAP_SIZE = TACH_TICK_LEN;
|
||||
|
||||
// Bits in control register
|
||||
enum control_bits : unsigned
|
||||
@ -110,10 +111,8 @@ enum {
|
||||
};
|
||||
|
||||
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)
|
||||
: device_t(mconfig , HP_1MA6 , tag , owner , clock)
|
||||
, m_tape(*this , "drive")
|
||||
{
|
||||
clear_state();
|
||||
}
|
||||
@ -141,7 +140,7 @@ READ8_MEMBER(hp_1ma6_device::reg_r)
|
||||
switch (offset) {
|
||||
case 0:
|
||||
// Status register
|
||||
update_tape_pos();
|
||||
m_tape->update_speed_pos();
|
||||
if (m_cmd_state == CMD_IDLE) {
|
||||
BIT_SET(m_status_reg , STS_READY_BIT);
|
||||
} else if (m_cmd_state == CMD_STOPPING ||
|
||||
@ -151,24 +150,15 @@ READ8_MEMBER(hp_1ma6_device::reg_r)
|
||||
}
|
||||
if ((m_control_reg & (BIT_MASK(CTL_WRITE_SYNC_BIT) |
|
||||
BIT_MASK(CTL_WRITE_GAP_BIT))) != 0 &&
|
||||
(!m_cartridge_in || !is_readonly())) {
|
||||
(m_tape->cart_out_r() || !m_tape->wpr_r())) {
|
||||
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) {
|
||||
if (m_cmd_state == CMD_IDLE ||
|
||||
m_tape->gap_reached(MIN_GAP_SIZE)) {
|
||||
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;
|
||||
@ -177,7 +167,7 @@ READ8_MEMBER(hp_1ma6_device::reg_r)
|
||||
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) {
|
||||
if (!m_tape->cart_out_r()) {
|
||||
BIT_SET(m_status_reg , STS_CASSETTE_IN_BIT);
|
||||
}
|
||||
break;
|
||||
@ -191,143 +181,137 @@ READ8_MEMBER(hp_1ma6_device::reg_r)
|
||||
return res;
|
||||
}
|
||||
|
||||
void hp_1ma6_device::device_start()
|
||||
WRITE_LINE_MEMBER(hp_1ma6_device::cart_out_w)
|
||||
{
|
||||
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);
|
||||
LOG_DBG("cart_out_w %d\n" , state);
|
||||
if (state) {
|
||||
// STS_CASSETTE_IN_BIT is set by reading status register
|
||||
BIT_CLR(m_status_reg , STS_CASSETTE_IN_BIT);
|
||||
}
|
||||
}
|
||||
|
||||
void hp_1ma6_device::device_reset()
|
||||
WRITE_LINE_MEMBER(hp_1ma6_device::hole_w)
|
||||
{
|
||||
clear_state();
|
||||
|
||||
m_tape_timer->reset();
|
||||
m_hole_timer->reset();
|
||||
if (state) {
|
||||
LOG_DBG("hole_w\n");
|
||||
BIT_SET(m_status_reg , STS_HOLE_BIT);
|
||||
}
|
||||
}
|
||||
|
||||
void hp_1ma6_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr)
|
||||
WRITE_LINE_MEMBER(hp_1ma6_device::tacho_tick_w)
|
||||
{
|
||||
update_tape_pos();
|
||||
if (state) {
|
||||
LOG_DBG("tacho_tick_w\n");
|
||||
BIT_SET(m_status_reg , STS_TACH_BIT);
|
||||
}
|
||||
}
|
||||
|
||||
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:
|
||||
WRITE_LINE_MEMBER(hp_1ma6_device::motion_w)
|
||||
{
|
||||
if (state) {
|
||||
LOG_DBG("motion_w @%.6f st=%d\n" , machine().time().as_double() , m_cmd_state);
|
||||
switch (m_cmd_state) {
|
||||
case CMD_STOPPING:
|
||||
if (!m_tape->is_moving()) {
|
||||
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 , is_moving_fwd());
|
||||
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 CMD_STARTING:
|
||||
{
|
||||
hp_dc100_tape_device::tape_op_t op = hp_dc100_tape_device::OP_IDLE;
|
||||
|
||||
switch (m_control_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:
|
||||
if (m_tape->is_above_threshold()) {
|
||||
// Start RD
|
||||
m_cmd_state = CMD_RD_WAIT_SYNC;
|
||||
op = hp_dc100_tape_device::OP_READ;
|
||||
}
|
||||
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):
|
||||
// Start simple movement
|
||||
m_cmd_state = CMD_FAST_FWD_REV;
|
||||
break;
|
||||
|
||||
case BIT_MASK(CTL_WRITE_DATA_BIT):
|
||||
if (m_tape->is_above_threshold()) {
|
||||
// Start re-writing
|
||||
m_cmd_state = CMD_WR_WAIT_SYNC;
|
||||
// Need to achieve sync first (hence RD op)
|
||||
op = hp_dc100_tape_device::OP_READ;
|
||||
}
|
||||
break;
|
||||
|
||||
case BIT_MASK(CTL_WRITE_SYNC_BIT):
|
||||
if (m_tape->is_above_threshold()) {
|
||||
// Start WR
|
||||
load_wr_word();
|
||||
m_cmd_state = CMD_WR_PREAMBLE;
|
||||
op = hp_dc100_tape_device::OP_WRITE;
|
||||
}
|
||||
break;
|
||||
|
||||
case BIT_MASK(CTL_WRITE_SYNC_BIT) | BIT_MASK(CTL_WRITE_GAP_BIT):
|
||||
if (m_tape->is_above_threshold()) {
|
||||
// Start erasing (gap writing)
|
||||
m_cmd_state = CMD_WR_GAP;
|
||||
op = hp_dc100_tape_device::OP_ERASE;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
m_tape->set_op(op);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
WRITE_LINE_MEMBER(hp_1ma6_device::rd_bit_w)
|
||||
{
|
||||
LOG_RW("RD bit %d (st=%d,sr=%02x,i=%u)\n" , state , m_cmd_state , m_data_sr , m_bit_idx);
|
||||
switch (m_cmd_state) {
|
||||
case CMD_RD_WAIT_SYNC:
|
||||
if (state) {
|
||||
// Got sync (bit is 1)
|
||||
LOG_RW("RD synced\n");
|
||||
m_cmd_state = CMD_RD;
|
||||
m_bit_idx = 7;
|
||||
m_data_sr = 0;
|
||||
}
|
||||
break;
|
||||
|
||||
case HOLE_TMR_ID:
|
||||
BIT_SET(m_status_reg , STS_HOLE_BIT);
|
||||
m_hole_timer->adjust(time_to_next_hole());
|
||||
case CMD_RD:
|
||||
if (state) {
|
||||
BIT_SET(m_data_sr , m_bit_idx);
|
||||
}
|
||||
if (m_bit_idx) {
|
||||
m_bit_idx--;
|
||||
} else {
|
||||
LOG_RW("RD byte %02x\n" , m_data_sr);
|
||||
m_data_reg = m_data_sr;
|
||||
m_bit_idx = 7;
|
||||
m_data_sr = 0;
|
||||
BIT_SET(m_status_reg , STS_READY_BIT);
|
||||
}
|
||||
break;
|
||||
|
||||
case CMD_WR_WAIT_SYNC:
|
||||
m_cmd_state = CMD_WR_PREAMBLE;
|
||||
load_wr_word();
|
||||
m_tape->set_op(hp_dc100_tape_device::OP_WRITE);
|
||||
break;
|
||||
|
||||
default:
|
||||
@ -335,74 +319,50 @@ void hp_1ma6_device::device_timer(emu_timer &timer, device_timer_id id, int para
|
||||
}
|
||||
}
|
||||
|
||||
image_init_result hp_1ma6_device::call_load()
|
||||
READ_LINE_MEMBER(hp_1ma6_device::wr_bit_r)
|
||||
{
|
||||
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) ? '>' : ' ';
|
||||
bool bit = m_cmd_state == CMD_WR_PREAMBLE ? false : BIT(m_data_sr , m_bit_idx);
|
||||
if (m_bit_idx) {
|
||||
m_bit_idx--;
|
||||
} else {
|
||||
m1 = '<';
|
||||
m2 = BIT(m_control_reg , CTL_FAST_BIT) ? '<' : ' ';
|
||||
if (m_cmd_state == CMD_WR) {
|
||||
load_wr_word();
|
||||
}
|
||||
m_cmd_state = CMD_WR;
|
||||
m_bit_idx = 7;
|
||||
}
|
||||
|
||||
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;
|
||||
LOG_RW("WR bit %d (sr=%02x,i=%u)\n" , bit , m_data_sr , m_bit_idx);
|
||||
return bit;
|
||||
}
|
||||
|
||||
const char *hp_1ma6_device::file_extensions() const
|
||||
void hp_1ma6_device::device_add_mconfig(machine_config &config)
|
||||
{
|
||||
return "hti";
|
||||
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_LEN);
|
||||
m_tape->set_bits_per_word(16);
|
||||
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));
|
||||
m_tape->tacho_tick().set(FUNC(hp_1ma6_device::tacho_tick_w));
|
||||
m_tape->motion_event().set(FUNC(hp_1ma6_device::motion_w));
|
||||
m_tape->rd_bit().set(FUNC(hp_1ma6_device::rd_bit_w));
|
||||
m_tape->wr_bit().set(FUNC(hp_1ma6_device::wr_bit_r));
|
||||
}
|
||||
|
||||
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_bit_idx));
|
||||
save_item(NAME(m_data_sr));
|
||||
}
|
||||
|
||||
void hp_1ma6_device::device_reset()
|
||||
{
|
||||
clear_state();
|
||||
}
|
||||
|
||||
void hp_1ma6_device::clear_state()
|
||||
@ -410,300 +370,40 @@ 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_bit_idx = 0;
|
||||
m_data_sr = 0;
|
||||
m_cmd_state = CMD_IDLE;
|
||||
set_cartridge_in(false);
|
||||
set_cartridge_in(is_loaded());
|
||||
}
|
||||
|
||||
unsigned hp_1ma6_device::current_track() const
|
||||
void hp_1ma6_device::load_wr_word()
|
||||
{
|
||||
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);
|
||||
m_bit_idx = 7;
|
||||
m_data_sr = m_data_reg;
|
||||
LOG_RW("WR byte %02x\n" , m_data_sr);
|
||||
BIT_SET(m_status_reg , STS_READY_BIT);
|
||||
}
|
||||
|
||||
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;
|
||||
if (!BIT(new_ctl_reg , CTL_POWER_UP_BIT) ||
|
||||
!BIT(new_ctl_reg , CTL_MOTOR_ON_BIT)) {
|
||||
m_cmd_state = CMD_STOPPING;
|
||||
} else {
|
||||
m_cmd_state = CMD_STARTING;
|
||||
}
|
||||
|
||||
m_tape_timer->adjust(cmd_duration);
|
||||
}
|
||||
m_tape->set_op(hp_dc100_tape_device::OP_IDLE);
|
||||
m_tape->set_track_no(BIT(m_control_reg , CTL_TRACK_NO_BIT));
|
||||
|
||||
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;
|
||||
hp_dc100_tape_device::tape_speed_t new_speed = hp_dc100_tape_device::SP_STOP;
|
||||
if (BIT(new_ctl_reg , CTL_POWER_UP_BIT) &&
|
||||
BIT(new_ctl_reg , CTL_MOTOR_ON_BIT)) {
|
||||
new_speed = BIT(new_ctl_reg , CTL_FAST_BIT) ? hp_dc100_tape_device::SP_FAST :
|
||||
hp_dc100_tape_device::SP_SLOW;
|
||||
}
|
||||
|
||||
m_image_dirty = false;
|
||||
set_cartridge_in(true);
|
||||
|
||||
return image_init_result::PASS;
|
||||
m_tape->set_speed_setpoint(new_speed , BIT(new_ctl_reg , CTL_DIR_FWD_BIT));
|
||||
motion_w(1);
|
||||
}
|
||||
|
@ -14,9 +14,9 @@
|
||||
#pragma once
|
||||
|
||||
#include "formats/hti_tape.h"
|
||||
#include "machine/hp_dc100_tape.h"
|
||||
|
||||
class hp_1ma6_device : public device_t ,
|
||||
public device_image_interface
|
||||
class hp_1ma6_device : public device_t
|
||||
{
|
||||
public:
|
||||
// construction/destruction
|
||||
@ -26,89 +26,49 @@ public:
|
||||
DECLARE_WRITE8_MEMBER(reg_w);
|
||||
DECLARE_READ8_MEMBER(reg_r);
|
||||
|
||||
DECLARE_WRITE_LINE_MEMBER(cart_out_w);
|
||||
DECLARE_WRITE_LINE_MEMBER(hole_w);
|
||||
DECLARE_WRITE_LINE_MEMBER(tacho_tick_w);
|
||||
DECLARE_WRITE_LINE_MEMBER(motion_w);
|
||||
DECLARE_WRITE_LINE_MEMBER(rd_bit_w);
|
||||
DECLARE_READ_LINE_MEMBER(wr_bit_r);
|
||||
|
||||
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;
|
||||
|
||||
// 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:
|
||||
required_device<hp_dc100_tape_device> m_tape;
|
||||
|
||||
// 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;
|
||||
unsigned m_bit_idx;
|
||||
uint8_t m_data_sr;
|
||||
|
||||
// Command FSM state
|
||||
typedef enum {
|
||||
CMD_IDLE,
|
||||
CMD_STOPPING,
|
||||
CMD_STARTING,
|
||||
CMD_RD_WAIT_SYNC,
|
||||
CMD_RD_MSB,
|
||||
CMD_RD_LSB,
|
||||
CMD_WR_SYNC,
|
||||
CMD_WR_MSB,
|
||||
CMD_WR_LSB,
|
||||
CMD_RD,
|
||||
CMD_WR_WAIT_SYNC,
|
||||
CMD_WR_PREAMBLE,
|
||||
CMD_WR,
|
||||
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 load_wr_word();
|
||||
void start_cmd_exec(uint8_t new_ctl_reg);
|
||||
image_init_result internal_load(bool is_create);
|
||||
};
|
||||
|
||||
// device type definition
|
||||
|
914
src/devices/machine/hp_dc100_tape.cpp
Normal file
914
src/devices/machine/hp_dc100_tape.cpp
Normal file
@ -0,0 +1,914 @@
|
||||
// license:BSD-3-Clause
|
||||
// copyright-holders:F. Ulivi
|
||||
/*********************************************************************
|
||||
|
||||
hp_dc100_tape.cpp
|
||||
|
||||
HP DC100 tape cartridge & drive
|
||||
|
||||
This driver emulates the DC100 tape drive as used by HP9825, 9845
|
||||
and 85 systems.
|
||||
|
||||
Main features:
|
||||
- Handles HTI tape images
|
||||
- Supports 3 speed setpoints (stop, slow & fast) and forward/reverse
|
||||
tape direction
|
||||
- Applies a constant acceleration whenever a change of speed setpoint
|
||||
and/or direction occurs
|
||||
- Reports through a callback "motion" events such as the reaching/leaving
|
||||
of setpoint, the crossing of speed thresholds, etc.
|
||||
- Simulates tachometer ticks
|
||||
- Handles 3 different tape operations: reading, writing and erasing.
|
||||
- Simulates the serial reading and writing of bits from/to tape
|
||||
and their correct timing
|
||||
- Reports various tape conditions as the presence of cartridge,
|
||||
of tape holes, of the write-protection switch etc.
|
||||
- Checks for gaps (erased zones) of arbitrary length
|
||||
|
||||
*********************************************************************/
|
||||
|
||||
#include "emu.h"
|
||||
#include "hp_dc100_tape.h"
|
||||
|
||||
// Debugging
|
||||
#include "logmacro.h"
|
||||
#define LOG_TMR_MASK (LOG_GENERAL << 1)
|
||||
#define LOG_TMR(...) LOGMASKED(LOG_TMR_MASK, __VA_ARGS__)
|
||||
#define LOG_DBG_MASK (LOG_TMR_MASK << 1)
|
||||
#define LOG_DBG(...) LOGMASKED(LOG_DBG_MASK, __VA_ARGS__)
|
||||
#define LOG_RW_MASK (LOG_DBG_MASK << 1)
|
||||
#define LOG_RW(...) LOGMASKED(LOG_RW_MASK, __VA_ARGS__)
|
||||
#undef VERBOSE
|
||||
//#define VERBOSE (LOG_GENERAL | LOG_TMR_MASK | LOG_DBG_MASK | LOG_RW_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);
|
||||
}
|
||||
}
|
||||
|
||||
// Device type definition
|
||||
DEFINE_DEVICE_TYPE(HP_DC100_TAPE, hp_dc100_tape_device, "hp_dc100_tape", "HP DC100 tape drive")
|
||||
|
||||
// Timers
|
||||
enum {
|
||||
BIT_TMR_ID,
|
||||
TACHO_TMR_ID,
|
||||
HOLE_TMR_ID,
|
||||
MOTION_TMR_ID
|
||||
};
|
||||
|
||||
// Constants
|
||||
constexpr double MOTION_MARGIN = 1e-5; // Margin to ensure motion events have passed when timer expires (10 µs)
|
||||
constexpr hti_format_t::tape_pos_t TAPE_INIT_POS = 80 * hti_format_t::ONE_INCH_POS; // Initial tape position: 80" from beginning (just past the punched part)
|
||||
|
||||
hp_dc100_tape_device::hp_dc100_tape_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
|
||||
: device_t(mconfig , HP_DC100_TAPE , tag , owner , clock)
|
||||
, device_image_interface(mconfig , *this)
|
||||
, m_cart_out_handler(*this)
|
||||
, m_hole_handler(*this)
|
||||
, m_tacho_tick_handler(*this)
|
||||
, m_motion_handler(*this)
|
||||
, m_rd_bit_handler(*this)
|
||||
, m_wr_bit_handler(*this)
|
||||
, m_image()
|
||||
, m_image_dirty(false)
|
||||
{
|
||||
}
|
||||
|
||||
image_init_result hp_dc100_tape_device::call_load()
|
||||
{
|
||||
return internal_load(false);
|
||||
}
|
||||
|
||||
image_init_result hp_dc100_tape_device::call_create(int format_type, util::option_resolution *format_options)
|
||||
{
|
||||
return internal_load(true);
|
||||
}
|
||||
|
||||
void hp_dc100_tape_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_tape_present(false);
|
||||
}
|
||||
|
||||
std::string hp_dc100_tape_device::call_display()
|
||||
{
|
||||
std::string buffer;
|
||||
// Mostly lifted from cassette_image_device::call_display ;)
|
||||
|
||||
// Do not show anything if image not loaded or tape not moving
|
||||
if (cart_out_r() || !has_started()) {
|
||||
return buffer;
|
||||
}
|
||||
|
||||
char track = m_track ? 'B' : 'A';
|
||||
char r_w = m_current_op == OP_WRITE || m_current_op == OP_ERASE ? 'W' : 'R';
|
||||
char m1;
|
||||
char m2;
|
||||
|
||||
if (is_moving_fwd()) {
|
||||
m1 = '>';
|
||||
m2 = m_tape_speed == SP_FAST ? '>' : ' ';
|
||||
} else {
|
||||
m1 = '<';
|
||||
m2 = m_tape_speed == SP_FAST ? '<' : ' ';
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
const char *hp_dc100_tape_device::file_extensions() const
|
||||
{
|
||||
return "hti";
|
||||
}
|
||||
|
||||
void hp_dc100_tape_device::set_acceleration(double accel)
|
||||
{
|
||||
m_acceleration = accel;
|
||||
}
|
||||
|
||||
void hp_dc100_tape_device::set_set_points(double slow_sp , double fast_sp)
|
||||
{
|
||||
m_slow_set_point = slow_sp;
|
||||
m_fast_set_point = fast_sp;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
m_image.set_bits_per_word(bits);
|
||||
}
|
||||
|
||||
void hp_dc100_tape_device::set_go_threshold(double threshold)
|
||||
{
|
||||
m_go_threshold = threshold;
|
||||
}
|
||||
|
||||
void hp_dc100_tape_device::set_track_no(unsigned track)
|
||||
{
|
||||
if (m_track != track) {
|
||||
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) {
|
||||
// Close current op on old track
|
||||
set_op(OP_IDLE);
|
||||
}
|
||||
m_track = track;
|
||||
if (saved_op != OP_IDLE) {
|
||||
// Resume op on new track
|
||||
set_op(saved_op);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool hp_dc100_tape_device::set_speed_setpoint(tape_speed_t speed , bool fwd)
|
||||
{
|
||||
if (!m_present) {
|
||||
return false;
|
||||
}
|
||||
|
||||
double new_setpoint = compute_set_point(speed, fwd);
|
||||
|
||||
if (m_set_point != new_setpoint) {
|
||||
update_speed_pos();
|
||||
LOG_DBG("Speed SP changed %f->%f %.6f p=%d\n" , m_set_point , new_setpoint , machine().time().as_double() , m_tape_pos);
|
||||
m_tape_speed = speed;
|
||||
m_set_point = new_setpoint;
|
||||
// Speed set point changed, accelerate/decelerate
|
||||
m_accelerating = true;
|
||||
if (m_start_time.is_never()) {
|
||||
// Tape starting now
|
||||
start_tape();
|
||||
// When tape starts m_current_op is always OP_IDLE
|
||||
m_gap_detect_start = m_tape_pos;
|
||||
}
|
||||
adjust_tacho_timer();
|
||||
adjust_hole_timer();
|
||||
set_motion_timer();
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void hp_dc100_tape_device::set_op(tape_op_t op , bool force)
|
||||
{
|
||||
if (!m_present || m_start_time.is_never()) {
|
||||
return;
|
||||
}
|
||||
if (!m_in_set_op && (op != m_current_op || force)) {
|
||||
LOG_DBG("Op %d->%d (f=%d)\n" , m_current_op , op , force);
|
||||
m_in_set_op = true;
|
||||
update_speed_pos();
|
||||
auto prev_op = m_current_op;
|
||||
// Close current operating state
|
||||
stop_op();
|
||||
m_in_set_op = false;
|
||||
m_current_op = op;
|
||||
|
||||
switch (op) {
|
||||
case OP_IDLE:
|
||||
m_gap_detect_start = m_tape_pos;
|
||||
break;
|
||||
|
||||
case OP_READ:
|
||||
if (prev_op == OP_WRITE) {
|
||||
LOG("Starting RD after WR?\n");
|
||||
}
|
||||
if (fabs(m_speed) < m_slow_set_point) {
|
||||
LOG("Starting RD at %f speed?\n" , m_speed);
|
||||
}
|
||||
m_rd_it_valid = m_image.next_data(get_track_no() , m_tape_pos , is_moving_fwd() , false , m_rd_it);
|
||||
load_rd_word();
|
||||
m_gap_detect_start = m_tape_pos;
|
||||
break;
|
||||
|
||||
case OP_WRITE:
|
||||
if (m_accelerating || fabs(m_speed) != m_slow_set_point) {
|
||||
LOG("Starting WR at %f speed (acc=%d)?\n" , m_speed , m_accelerating);
|
||||
}
|
||||
if (prev_op == OP_READ) {
|
||||
// Switching from RD to WR
|
||||
// Clear out the part of m_rw_word that is going to be written
|
||||
LOG_RW("Switch RD->WR @%d, idx=%d, w=%04x\n" , m_rw_pos , m_bit_idx , m_rw_word);
|
||||
if (is_moving_fwd()) {
|
||||
if (--m_bit_idx >= 0) {
|
||||
m_rw_word &= 0xffffU << (m_bit_idx + 1);
|
||||
} else {
|
||||
m_bit_idx = 15;
|
||||
m_rw_pos = m_next_bit_pos;
|
||||
m_rw_word = 0;
|
||||
}
|
||||
} else {
|
||||
if (++m_bit_idx < 16) {
|
||||
m_rw_word &= 0xffffU >> (16 - m_bit_idx);
|
||||
} else {
|
||||
m_bit_idx = 0;
|
||||
m_rw_pos = m_next_bit_pos;
|
||||
m_rw_word = 0;
|
||||
}
|
||||
}
|
||||
time_to_distance(m_next_bit_pos - m_tape_pos, m_next_bit_pos, m_bit_timer);
|
||||
} else {
|
||||
m_rw_word = 0;
|
||||
if (is_moving_fwd()) {
|
||||
m_bit_idx = 15;
|
||||
} else {
|
||||
m_bit_idx = 0;
|
||||
}
|
||||
m_next_bit_pos = m_rw_pos = m_tape_pos;
|
||||
m_bit_timer->adjust(attotime::zero);
|
||||
}
|
||||
break;
|
||||
|
||||
case OP_ERASE:
|
||||
LOG_DBG("Start GAP @%d\n" , m_tape_pos);
|
||||
m_rw_pos = m_tape_pos;
|
||||
break;
|
||||
|
||||
default:
|
||||
LOG("Invalid op!\n");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void hp_dc100_tape_device::update_speed_pos()
|
||||
{
|
||||
if (m_start_time.is_never()) {
|
||||
// Tape stopped
|
||||
return;
|
||||
}
|
||||
|
||||
attotime delta_time{machine().time() - m_start_time};
|
||||
if (delta_time.is_zero()) {
|
||||
return;
|
||||
}
|
||||
m_start_time = machine().time();
|
||||
double delta_time_double = delta_time.as_double();
|
||||
|
||||
// Space in const A phase
|
||||
double space_const_a;
|
||||
// Time in const V phase
|
||||
double time_const_v;
|
||||
// Report a motion event
|
||||
bool motion_event = false;
|
||||
// Direction inverted
|
||||
bool inverted = false;
|
||||
|
||||
if (m_accelerating) {
|
||||
auto prev_speed = m_speed;
|
||||
double time_to_const_v = fabs(m_set_point - m_speed) / m_acceleration;
|
||||
double acceleration = m_set_point > m_speed ? m_acceleration : -m_acceleration;
|
||||
bool retrigger_motion = false;
|
||||
if (delta_time_double < time_to_const_v) {
|
||||
space_const_a = const_a_space(acceleration , delta_time_double);
|
||||
m_speed += delta_time_double * acceleration;
|
||||
time_const_v = 0.0;
|
||||
} else {
|
||||
space_const_a = const_a_space(acceleration , time_to_const_v);
|
||||
time_const_v = delta_time_double - time_to_const_v;
|
||||
LOG_DBG("Acceleration ends\n");
|
||||
m_accelerating = false;
|
||||
m_speed = m_set_point;
|
||||
motion_event = true;
|
||||
retrigger_motion = true;
|
||||
if (m_speed == 0.0) {
|
||||
// Tape stops
|
||||
stop_tape();
|
||||
}
|
||||
}
|
||||
if ((fabs(prev_speed) - m_slow_set_point) * (fabs(m_speed) - m_slow_set_point) <= 0.0 ||
|
||||
(fabs(prev_speed) - m_go_threshold) * (fabs(m_speed) - m_go_threshold) < 0.0) {
|
||||
// Slow speed threshold crossed
|
||||
// In-motion threshold crossed
|
||||
LOG_DBG("Thr crossed %f->%f\n" , prev_speed , m_speed);
|
||||
motion_event = true;
|
||||
retrigger_motion = true;
|
||||
}
|
||||
if (prev_speed * m_speed < 0.0) {
|
||||
// Direction inverted (speed sign flipped)
|
||||
LOG_DBG("Dir inverted s=%f\n" , m_speed);
|
||||
inverted = true;
|
||||
retrigger_motion = true;
|
||||
}
|
||||
if (retrigger_motion) {
|
||||
set_motion_timer();
|
||||
}
|
||||
} else {
|
||||
space_const_a = 0.0;
|
||||
time_const_v = delta_time_double;
|
||||
}
|
||||
|
||||
hti_format_t::tape_pos_t delta_pos = (hti_format_t::tape_pos_t)((space_const_a + m_speed * time_const_v) * hti_format_t::ONE_INCH_POS);
|
||||
LOG_DBG("dp=%d\n" , delta_pos);
|
||||
if (!hti_format_t::pos_offset(m_tape_pos , true , delta_pos)) {
|
||||
LOG("Tape unspooled!\n");
|
||||
}
|
||||
|
||||
if (inverted) {
|
||||
adjust_tacho_timer();
|
||||
adjust_hole_timer();
|
||||
if (m_gap_detect_start != hti_format_t::NULL_TAPE_POS) {
|
||||
m_gap_detect_start = m_tape_pos;
|
||||
}
|
||||
}
|
||||
if (motion_event) {
|
||||
// Must be done here or pos & speed won't be correct
|
||||
m_motion_handler(1);
|
||||
}
|
||||
}
|
||||
|
||||
hti_format_t::tape_pos_t hp_dc100_tape_device::get_approx_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_pos = (hti_format_t::tape_pos_t)(delta_time.as_double() * m_speed * hti_format_t::ONE_INCH_POS);
|
||||
auto tape_pos = m_tape_pos;
|
||||
hti_format_t::pos_offset(tape_pos , true , delta_pos);
|
||||
return tape_pos;
|
||||
}
|
||||
|
||||
bool hp_dc100_tape_device::gap_reached(hti_format_t::tape_pos_t min_gap_size)
|
||||
{
|
||||
update_speed_pos();
|
||||
|
||||
if (m_gap_detect_start != hti_format_t::NULL_TAPE_POS &&
|
||||
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(get_track_no() , tmp , m_tape_pos)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void hp_dc100_tape_device::time_to_next_gap(hti_format_t::tape_pos_t min_gap_size , bool new_gap , emu_timer *target_timer)
|
||||
{
|
||||
update_speed_pos();
|
||||
|
||||
bool fwd = is_moving_fwd();
|
||||
hti_format_t::tape_pos_t tmp = m_tape_pos;
|
||||
bool found = true;
|
||||
if (new_gap) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
if (found && m_image.next_gap(get_track_no() , tmp , fwd , min_gap_size)) {
|
||||
hti_format_t::tape_pos_t dummy;
|
||||
LOG_DBG("TTNG T%u S%d N%d %d->%d\n" , get_track_no() , min_gap_size , new_gap , m_tape_pos , tmp);
|
||||
time_to_distance(tmp - m_tape_pos, dummy, target_timer);
|
||||
} else {
|
||||
LOG_DBG("TTNG T%u S%d N%d %d->X\n" , get_track_no() , min_gap_size , new_gap , m_tape_pos);
|
||||
target_timer->reset();
|
||||
}
|
||||
}
|
||||
|
||||
void hp_dc100_tape_device::device_start()
|
||||
{
|
||||
m_cart_out_handler.resolve_safe();
|
||||
m_hole_handler.resolve_safe();
|
||||
m_tacho_tick_handler.resolve_safe();
|
||||
m_motion_handler.resolve_safe();
|
||||
m_rd_bit_handler.resolve_safe();
|
||||
m_wr_bit_handler.resolve_safe(0);
|
||||
|
||||
save_item(NAME(m_acceleration));
|
||||
save_item(NAME(m_slow_set_point));
|
||||
save_item(NAME(m_fast_set_point));
|
||||
save_item(NAME(m_tick_size));
|
||||
save_item(NAME(m_go_threshold));
|
||||
save_item(NAME(m_tape_pos));
|
||||
save_item(NAME(m_set_point));
|
||||
save_item(NAME(m_speed));
|
||||
save_item(NAME(m_start_time));
|
||||
save_item(NAME(m_accelerating));
|
||||
save_item(NAME(m_track));
|
||||
save_item(NAME(m_in_set_op));
|
||||
save_item(NAME(m_present));
|
||||
save_item(NAME(m_rd_it_valid));
|
||||
save_item(NAME(m_rw_word));
|
||||
save_item(NAME(m_bit_idx));
|
||||
save_item(NAME(m_rw_pos));
|
||||
save_item(NAME(m_gap_detect_start));
|
||||
save_item(NAME(m_next_bit_pos));
|
||||
save_item(NAME(m_next_tacho_pos));
|
||||
save_item(NAME(m_next_hole_pos));
|
||||
save_item(NAME(m_image_dirty));
|
||||
|
||||
m_bit_timer = timer_alloc(BIT_TMR_ID);
|
||||
m_tacho_timer = timer_alloc(TACHO_TMR_ID);
|
||||
m_hole_timer = timer_alloc(HOLE_TMR_ID);
|
||||
m_motion_timer = timer_alloc(MOTION_TMR_ID);
|
||||
}
|
||||
|
||||
void hp_dc100_tape_device::device_reset()
|
||||
{
|
||||
clear_state();
|
||||
}
|
||||
|
||||
void hp_dc100_tape_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr)
|
||||
{
|
||||
LOG_TMR("%.6f TMR %d p=%d s=%.3f(%.3f) a=%d\n" , machine().time().as_double() , id , m_tape_pos , m_speed , m_set_point , m_accelerating);
|
||||
update_speed_pos();
|
||||
|
||||
switch (id) {
|
||||
case BIT_TMR_ID:
|
||||
m_tape_pos = m_next_bit_pos;
|
||||
if (m_current_op == OP_READ) {
|
||||
bool bit = BIT(m_rd_it->second , m_bit_idx);
|
||||
m_rd_bit_handler(bit);
|
||||
if (is_moving_fwd()) {
|
||||
if (--m_bit_idx >= 0) {
|
||||
time_to_distance(m_image.bit_length(BIT(m_rd_it->second , m_bit_idx)), m_next_bit_pos, m_bit_timer);
|
||||
} else {
|
||||
m_rd_it_valid = m_image.adv_it(get_track_no() , true , m_rd_it) != hti_format_t::ADV_NO_MORE_DATA;
|
||||
load_rd_word();
|
||||
}
|
||||
} else {
|
||||
if (++m_bit_idx < 16) {
|
||||
time_to_distance(-m_image.bit_length(BIT(m_rd_it->second , m_bit_idx)), m_next_bit_pos, m_bit_timer);
|
||||
} else {
|
||||
m_rd_it_valid = m_image.adv_it(get_track_no() , false , m_rd_it) != hti_format_t::ADV_NO_MORE_DATA;
|
||||
load_rd_word();
|
||||
}
|
||||
}
|
||||
} else if (m_current_op == OP_WRITE) {
|
||||
bool bit = m_wr_bit_handler();
|
||||
hti_format_t::tape_pos_t bit_len = m_image.bit_length(bit);
|
||||
if (bit) {
|
||||
BIT_SET(m_rw_word , m_bit_idx);
|
||||
}
|
||||
if (is_moving_fwd()) {
|
||||
if (--m_bit_idx < 0) {
|
||||
store_wr_word();
|
||||
}
|
||||
} else {
|
||||
if (++m_bit_idx >= 16) {
|
||||
store_wr_word();
|
||||
}
|
||||
bit_len = -bit_len;
|
||||
}
|
||||
time_to_distance(bit_len, m_next_bit_pos, m_bit_timer);
|
||||
}
|
||||
break;
|
||||
|
||||
case TACHO_TMR_ID:
|
||||
m_tape_pos = m_next_tacho_pos;
|
||||
m_tacho_tick_handler(1);
|
||||
adjust_tacho_timer();
|
||||
break;
|
||||
|
||||
case HOLE_TMR_ID:
|
||||
m_tape_pos = m_next_hole_pos;
|
||||
m_hole_handler(1);
|
||||
adjust_hole_timer();
|
||||
break;
|
||||
|
||||
case MOTION_TMR_ID:
|
||||
// In itself it does nothing (all work is in update_speed_pos)
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void hp_dc100_tape_device::clear_state()
|
||||
{
|
||||
m_tape_pos = TAPE_INIT_POS;
|
||||
m_tape_speed = SP_STOP;
|
||||
m_set_point = 0.0;
|
||||
m_speed = 0.0;
|
||||
m_start_time = attotime::never;
|
||||
m_accelerating = false;
|
||||
m_track = 0;
|
||||
m_current_op = OP_IDLE;
|
||||
m_in_set_op = false;
|
||||
m_present = true;
|
||||
m_rd_it_valid = false;
|
||||
m_rw_word = 0;
|
||||
m_bit_idx = 0;
|
||||
m_rw_pos = hti_format_t::NULL_TAPE_POS;
|
||||
m_gap_detect_start = hti_format_t::NULL_TAPE_POS;
|
||||
m_bit_timer->reset();
|
||||
m_tacho_timer->reset();
|
||||
m_hole_timer->reset();
|
||||
m_motion_timer->reset();
|
||||
|
||||
set_tape_present(false);
|
||||
set_tape_present(is_loaded());
|
||||
}
|
||||
|
||||
image_init_result hp_dc100_tape_device::internal_load(bool is_create)
|
||||
{
|
||||
LOG("load %d\n" , 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)) {
|
||||
LOG("load failed\n");
|
||||
seterror(IMAGE_ERROR_INVALIDIMAGE , "Wrong format");
|
||||
set_tape_present(false);
|
||||
return image_init_result::FAIL;
|
||||
}
|
||||
LOG("load OK\n");
|
||||
|
||||
m_image_dirty = false;
|
||||
|
||||
set_tape_present(true);
|
||||
return image_init_result::PASS;
|
||||
}
|
||||
|
||||
void hp_dc100_tape_device::set_tape_present(bool present)
|
||||
{
|
||||
if (m_present != present) {
|
||||
m_present = present;
|
||||
m_cart_out_handler(!m_present);
|
||||
}
|
||||
}
|
||||
|
||||
double hp_dc100_tape_device::compute_set_point(tape_speed_t speed , bool fwd) const
|
||||
{
|
||||
double sp;
|
||||
|
||||
if (speed == SP_SLOW) {
|
||||
sp = m_slow_set_point;
|
||||
} else if (speed == SP_FAST) {
|
||||
sp = m_fast_set_point;
|
||||
} else {
|
||||
sp = 0.0;
|
||||
}
|
||||
|
||||
if (!fwd) {
|
||||
sp = -sp;
|
||||
}
|
||||
|
||||
return sp;
|
||||
}
|
||||
|
||||
void hp_dc100_tape_device::start_tape()
|
||||
{
|
||||
LOG_DBG("Tape started %.6f p=%d\n" , machine().time().as_double() , m_tape_pos);
|
||||
m_start_time = machine().time();
|
||||
m_accelerating = true;
|
||||
m_speed = 0;
|
||||
}
|
||||
|
||||
void hp_dc100_tape_device::stop_tape()
|
||||
{
|
||||
LOG_DBG("Tape stops %.6f p=%d\n" , machine().time().as_double() , m_tape_pos);
|
||||
m_start_time = attotime::never;
|
||||
m_accelerating = false;
|
||||
m_speed = 0;
|
||||
m_tacho_timer->reset();
|
||||
m_hole_timer->reset();
|
||||
m_motion_timer->reset();
|
||||
stop_op();
|
||||
}
|
||||
|
||||
double hp_dc100_tape_device::const_a_space(double a , double t) const
|
||||
{
|
||||
// Space traveled in time 't' at constant acceleration 'a' starting with 'm_speed' speed
|
||||
return t * (m_speed + a / 2 * t);
|
||||
}
|
||||
|
||||
attotime hp_dc100_tape_device::time_to_threshold(double threshold , bool zero_allowed) const
|
||||
{
|
||||
attotime time{ attotime::never };
|
||||
|
||||
auto delta_sp = m_set_point - m_speed;
|
||||
auto delta_t = threshold - m_speed;
|
||||
LOG_DBG("Dsp=%.6f D+th=%.6f\n" , delta_sp , delta_t);
|
||||
|
||||
if ((delta_sp * delta_t > 0.0 && fabs(delta_t) <= fabs(delta_sp)) ||
|
||||
(zero_allowed && delta_t == 0.0)) {
|
||||
time = attotime::from_double(fabs(delta_t) / m_acceleration);
|
||||
LOG_DBG("Time to +th: %.6f\n" , time.as_double());
|
||||
}
|
||||
|
||||
delta_t = -threshold - m_speed;
|
||||
LOG_DBG("Dsp=%.6f D-th=%.6f\n" , delta_sp , delta_t);
|
||||
if ((delta_sp * delta_t > 0.0 && fabs(delta_t) <= fabs(delta_sp)) ||
|
||||
(zero_allowed && delta_t == 0.0)) {
|
||||
double tm = fabs(delta_t) / m_acceleration;
|
||||
if (tm < time.as_double()) {
|
||||
time = attotime::from_double(tm);
|
||||
LOG_DBG("Time to -th: %.6f\n" , time.as_double());
|
||||
}
|
||||
}
|
||||
return time;
|
||||
}
|
||||
|
||||
void hp_dc100_tape_device::set_motion_timer()
|
||||
{
|
||||
if (!m_accelerating) {
|
||||
m_motion_timer->reset();
|
||||
} else {
|
||||
// There are 4 possible future "motion" events:
|
||||
// 1. Slow speed threshold is crossed
|
||||
// 2. The "in motion" threshold is crossed
|
||||
// 3. Tape direction reverses
|
||||
// 4. Set point is reached
|
||||
// Motion timer is set to expire at the event that occurs first
|
||||
attotime time{ time_to_threshold(m_slow_set_point , true) };
|
||||
|
||||
attotime tmp{ time_to_threshold(m_go_threshold , false) };
|
||||
if (tmp < time) {
|
||||
time = tmp;
|
||||
}
|
||||
|
||||
// Time to the moment when tape inverts its motion
|
||||
// (i.e. when m_speed crosses 0)
|
||||
tmp = time_to_threshold(0.0 , false);
|
||||
if (tmp < time) {
|
||||
time = tmp;
|
||||
}
|
||||
|
||||
// Time to reach set point
|
||||
tmp = attotime::from_double(fabs(m_speed - m_set_point) / m_acceleration);
|
||||
if (tmp < time) {
|
||||
time = tmp;
|
||||
}
|
||||
|
||||
if (time.is_never()) {
|
||||
// Should never get here
|
||||
m_motion_timer->reset();
|
||||
} else {
|
||||
// Add margin
|
||||
time += attotime::from_double(MOTION_MARGIN);
|
||||
|
||||
m_motion_timer->adjust(time);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void hp_dc100_tape_device::time_to_distance(hti_format_t::tape_pos_t distance , hti_format_t::tape_pos_t& target_pos , emu_timer *target_timer) const
|
||||
{
|
||||
if (m_start_time.is_never()) {
|
||||
// If tape is stopped we'll never get there..
|
||||
target_timer->reset();
|
||||
return;
|
||||
}
|
||||
|
||||
target_pos = m_tape_pos;
|
||||
if (!hti_format_t::pos_offset(target_pos , true , distance)) {
|
||||
// Beyond end of tape
|
||||
target_timer->reset();
|
||||
return;
|
||||
}
|
||||
|
||||
double space = double(distance) / hti_format_t::ONE_INCH_POS;
|
||||
double time_const_a;
|
||||
|
||||
if (m_accelerating) {
|
||||
// Time to reach constant V phase
|
||||
double time_to_const_v = fabs(m_set_point - m_speed) / m_acceleration;
|
||||
// Signed acceleration
|
||||
double acceleration = m_set_point > m_speed ? m_acceleration : -m_acceleration;
|
||||
// Compute time to cover distance with constant acceleration
|
||||
// It's the smallest non-negative root of this quadratic equation:
|
||||
// 1/2*acceleration*t^2+m_speed*t=space
|
||||
double delta = m_speed * m_speed + 2 * acceleration * space;
|
||||
bool has_root = delta >= 0.0;
|
||||
double time_in_const_a = 0.0;
|
||||
if (has_root) {
|
||||
double time_in_const_a_pos = (sqrt(delta) - m_speed) / acceleration;
|
||||
double time_in_const_a_neg = -(sqrt(delta) + m_speed) / acceleration;
|
||||
LOG_DBG("TTD %.6f %.6f\n" , time_in_const_a_pos , time_in_const_a_neg);
|
||||
if (time_in_const_a_pos >= 0.0) {
|
||||
if (time_in_const_a_neg >= 0.0) {
|
||||
// pos + neg +
|
||||
time_in_const_a = std::min(time_in_const_a_pos , time_in_const_a_neg);
|
||||
} else {
|
||||
// pos + neg -
|
||||
time_in_const_a = time_in_const_a_pos;
|
||||
}
|
||||
} else {
|
||||
if (time_in_const_a_neg >= 0.0) {
|
||||
// pos - neg +
|
||||
time_in_const_a = time_in_const_a_neg;
|
||||
} else {
|
||||
// pos - neg -
|
||||
has_root = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
LOG_DBG("TTD %d %d %.6f %.6f %.6f\n" , distance , has_root , m_speed , time_to_const_v , time_in_const_a);
|
||||
if (has_root && time_in_const_a <= time_to_const_v) {
|
||||
// Entirely in the constant A phase
|
||||
time_const_a = time_in_const_a;
|
||||
space = 0.0;
|
||||
} else {
|
||||
// Partly in const A & partly in const V
|
||||
double space_in_const_a = const_a_space(acceleration , time_to_const_v);
|
||||
space -= space_in_const_a;
|
||||
time_const_a = time_to_const_v;
|
||||
}
|
||||
} else {
|
||||
// Entirely in const V
|
||||
time_const_a = 0.0;
|
||||
}
|
||||
|
||||
// Time in constant V
|
||||
double time_const_v;
|
||||
if (space != 0.0) {
|
||||
if (m_set_point == 0.0) {
|
||||
target_timer->reset();
|
||||
return;
|
||||
} else {
|
||||
time_const_v = space / m_set_point;
|
||||
if (time_const_v < 0.0) {
|
||||
target_timer->reset();
|
||||
return;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
time_const_v = 0.0;
|
||||
}
|
||||
LOG_DBG("TTD %.6f %.6f\n" , time_const_a , time_const_v);
|
||||
|
||||
target_timer->adjust(attotime::from_double(time_const_a + time_const_v));
|
||||
}
|
||||
|
||||
void hp_dc100_tape_device::adjust_tacho_timer()
|
||||
{
|
||||
auto tick_fract = m_tape_pos % m_tick_size;
|
||||
hti_format_t::tape_pos_t dist_to_next;
|
||||
if (is_moving_fwd()) {
|
||||
// Distance to next tick in the fwd direction
|
||||
dist_to_next = m_tick_size - tick_fract;
|
||||
} else if (tick_fract) {
|
||||
// Distance to next tick in the rev direction when we're not exactly
|
||||
// on a tick (tick_fract != 0)
|
||||
dist_to_next = -tick_fract;
|
||||
} else {
|
||||
// Distance to next tick in the rev direction when we're exactly
|
||||
// on a tick (tick_fract == 0)
|
||||
dist_to_next = -m_tick_size;
|
||||
}
|
||||
LOG_DBG("Next tick @%d (pos=%d)\n" , dist_to_next , m_tape_pos);
|
||||
time_to_distance(dist_to_next, m_next_tacho_pos, m_tacho_timer);
|
||||
}
|
||||
|
||||
void hp_dc100_tape_device::adjust_hole_timer()
|
||||
{
|
||||
auto hole_pos = m_image.next_hole(m_tape_pos , is_moving_fwd());
|
||||
if (hole_pos == hti_format_t::NULL_TAPE_POS) {
|
||||
m_hole_timer->reset();
|
||||
} else {
|
||||
time_to_distance(hole_pos - m_tape_pos, m_next_hole_pos, m_hole_timer);
|
||||
}
|
||||
}
|
||||
|
||||
void hp_dc100_tape_device::stop_op()
|
||||
{
|
||||
if (m_current_op == OP_WRITE) {
|
||||
store_wr_word();
|
||||
} else if (m_current_op == OP_ERASE) {
|
||||
LOG_DBG("Wr gap from %d to %d\n" , m_rw_pos , m_tape_pos);
|
||||
m_image.write_gap(get_track_no() , m_rw_pos , m_tape_pos);
|
||||
m_image_dirty = true;
|
||||
}
|
||||
m_bit_timer->reset();
|
||||
m_current_op = OP_IDLE;
|
||||
m_gap_detect_start = hti_format_t::NULL_TAPE_POS;
|
||||
}
|
||||
|
||||
void hp_dc100_tape_device::load_rd_word()
|
||||
{
|
||||
if (m_rd_it_valid) {
|
||||
bool fwd = is_moving_fwd();
|
||||
if (fwd) {
|
||||
m_bit_idx = 15;
|
||||
} else {
|
||||
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);
|
||||
// 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) {
|
||||
bit_len = -bit_len;
|
||||
}
|
||||
time_to_distance(m_next_bit_pos + bit_len - m_tape_pos , m_next_bit_pos , m_bit_timer);
|
||||
LOG_RW("RD %04x @%d\n" , m_rd_it->second , m_next_bit_pos);
|
||||
} else {
|
||||
LOG_RW("End of RD data @%d\n" , m_tape_pos);
|
||||
stop_op();
|
||||
m_gap_detect_start = m_tape_pos;
|
||||
}
|
||||
}
|
||||
|
||||
void hp_dc100_tape_device::store_wr_word()
|
||||
{
|
||||
bool fwd = is_moving_fwd();
|
||||
if ((fwd && m_bit_idx == 15) ||
|
||||
(!fwd && m_bit_idx == 0)) {
|
||||
return;
|
||||
}
|
||||
hti_format_t::tape_pos_t word_length = m_image.word_length(m_rw_word);
|
||||
if (!fwd) {
|
||||
m_rw_pos -= word_length;
|
||||
}
|
||||
LOG_RW("WR %04x @%d\n" , m_rw_word , m_rw_pos);
|
||||
m_image.write_word(get_track_no() , m_rw_pos , m_rw_word , word_length , fwd);
|
||||
m_image_dirty = true;
|
||||
m_rw_word = 0;
|
||||
if (fwd) {
|
||||
m_bit_idx = 15;
|
||||
m_rw_pos += word_length;
|
||||
} else {
|
||||
m_bit_idx = 0;
|
||||
}
|
||||
}
|
178
src/devices/machine/hp_dc100_tape.h
Normal file
178
src/devices/machine/hp_dc100_tape.h
Normal file
@ -0,0 +1,178 @@
|
||||
// license:BSD-3-Clause
|
||||
// copyright-holders:F. Ulivi
|
||||
/*********************************************************************
|
||||
|
||||
hp_dc100_tape.h
|
||||
|
||||
HP DC100 tape cartridge & drive
|
||||
|
||||
*********************************************************************/
|
||||
|
||||
#ifndef MAME_MACHINE_HP_DC100_TAPE_H
|
||||
#define MAME_MACHINE_HP_DC100_TAPE_H
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "formats/hti_tape.h"
|
||||
|
||||
class hp_dc100_tape_device : public device_t,
|
||||
public device_image_interface
|
||||
{
|
||||
public:
|
||||
// Construction
|
||||
hp_dc100_tape_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock);
|
||||
|
||||
// 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;
|
||||
|
||||
// **** Units ****
|
||||
// Tape position or distance 1/(968 * 1024) in (see hti_format_t::tape_pos_t)
|
||||
// Speed in/s
|
||||
// Acceleration in/s^2
|
||||
|
||||
// Settings
|
||||
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_go_threshold(double threshold);
|
||||
|
||||
// Commands
|
||||
void set_track_no(unsigned track);
|
||||
|
||||
enum tape_speed_t {
|
||||
SP_STOP, // Stop
|
||||
SP_SLOW, // Slow speed
|
||||
SP_FAST // Fast speed
|
||||
};
|
||||
bool set_speed_setpoint(tape_speed_t speed , bool fwd);
|
||||
|
||||
enum tape_op_t {
|
||||
OP_IDLE, // No R/W
|
||||
OP_READ, // Read from tape
|
||||
OP_WRITE, // Write to tape
|
||||
OP_ERASE // Erase tape
|
||||
};
|
||||
void set_op(tape_op_t op , bool force = false);
|
||||
|
||||
void update_speed_pos();
|
||||
|
||||
// State access
|
||||
hti_format_t::tape_pos_t get_pos() const { return m_tape_pos; }
|
||||
hti_format_t::tape_pos_t get_approx_pos() const;
|
||||
double get_speed() const { return m_speed; }
|
||||
double get_speed_setpoint() const { return m_set_point; }
|
||||
unsigned get_track_no() const { return m_track; }
|
||||
tape_op_t get_op() const { return m_current_op; }
|
||||
bool has_started() const { return !m_start_time.is_never(); }
|
||||
bool is_moving() const { return fabs(m_speed) >= m_go_threshold; }
|
||||
bool is_accelerating() const { return m_accelerating; }
|
||||
bool is_moving_fwd() const { return m_speed == 0.0 ? m_set_point >= 0.0 : m_speed > 0.0; }
|
||||
bool cart_out_r() const { return !m_present; }
|
||||
bool wpr_r() const { return !m_present || is_readonly(); }
|
||||
bool is_above_threshold() const { return fabs(m_speed) >= m_slow_set_point; }
|
||||
bool gap_reached(hti_format_t::tape_pos_t min_gap_size);
|
||||
|
||||
void time_to_next_gap(hti_format_t::tape_pos_t min_gap_size , bool new_gap , emu_timer *target_timer);
|
||||
|
||||
// Callbacks
|
||||
// Cartridge is out (1)
|
||||
auto cart_out() { return m_cart_out_handler.bind(); }
|
||||
// On a hole (1)
|
||||
auto hole() { return m_hole_handler.bind(); }
|
||||
// Tachometer tick (1)
|
||||
auto tacho_tick() { return m_tacho_tick_handler.bind(); }
|
||||
// Motion event (1)
|
||||
// One of the following:
|
||||
// 1. "In motion" threshold crossed
|
||||
// 2. Slow speed threshold crossed
|
||||
// 3. Accelerated phase ended (== set point reached)
|
||||
auto motion_event() { return m_motion_handler.bind(); }
|
||||
// Read bit
|
||||
auto rd_bit() { return m_rd_bit_handler.bind(); }
|
||||
// Bit to write
|
||||
auto wr_bit() { return m_wr_bit_handler.bind(); }
|
||||
|
||||
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;
|
||||
|
||||
private:
|
||||
devcb_write_line m_cart_out_handler;
|
||||
devcb_write_line m_hole_handler;
|
||||
devcb_write_line m_tacho_tick_handler;
|
||||
devcb_write_line m_motion_handler;
|
||||
devcb_write_line m_rd_bit_handler;
|
||||
devcb_read_line m_wr_bit_handler;
|
||||
|
||||
// Settings
|
||||
double m_acceleration;
|
||||
double m_slow_set_point;
|
||||
double m_fast_set_point;
|
||||
hti_format_t::tape_pos_t m_tick_size;
|
||||
double m_go_threshold;
|
||||
|
||||
// State
|
||||
hti_format_t::tape_pos_t m_tape_pos;
|
||||
tape_speed_t m_tape_speed;
|
||||
double m_set_point;
|
||||
double m_speed;
|
||||
attotime m_start_time; // Tape moving if != never
|
||||
bool m_accelerating;
|
||||
unsigned m_track;
|
||||
tape_op_t m_current_op;
|
||||
bool m_in_set_op;
|
||||
bool m_present;
|
||||
hti_format_t::track_iterator_t m_rd_it;
|
||||
bool m_rd_it_valid;
|
||||
hti_format_t::tape_word_t m_rw_word;
|
||||
int m_bit_idx;
|
||||
hti_format_t::tape_pos_t m_rw_pos; // Start position of current R/W word or start position of gap when erasing
|
||||
hti_format_t::tape_pos_t m_gap_detect_start;
|
||||
|
||||
// Timers
|
||||
emu_timer *m_bit_timer;
|
||||
emu_timer *m_tacho_timer;
|
||||
emu_timer *m_hole_timer;
|
||||
emu_timer *m_motion_timer;
|
||||
hti_format_t::tape_pos_t m_next_bit_pos;
|
||||
hti_format_t::tape_pos_t m_next_tacho_pos;
|
||||
hti_format_t::tape_pos_t m_next_hole_pos;
|
||||
|
||||
// Image of tape
|
||||
hti_format_t m_image;
|
||||
bool m_image_dirty;
|
||||
|
||||
void clear_state();
|
||||
image_init_result internal_load(bool is_create);
|
||||
void set_tape_present(bool present);
|
||||
double compute_set_point(tape_speed_t speed , bool fwd) const;
|
||||
void start_tape();
|
||||
void stop_tape();
|
||||
double const_a_space(double a , double t) const;
|
||||
attotime time_to_threshold(double threshold , bool zero_allowed) const;
|
||||
void set_motion_timer();
|
||||
void time_to_distance(hti_format_t::tape_pos_t distance , hti_format_t::tape_pos_t& target_pos , emu_timer *target_timer) const;
|
||||
void adjust_tacho_timer();
|
||||
void adjust_hole_timer();
|
||||
void stop_op();
|
||||
void load_rd_word();
|
||||
void store_wr_word();
|
||||
};
|
||||
|
||||
// device type definition
|
||||
DECLARE_DEVICE_TYPE(HP_DC100_TAPE, hp_dc100_tape_device)
|
||||
|
||||
#endif // MAME_MACHINE_HP_DC100_TAPE_H
|
File diff suppressed because it is too large
Load Diff
@ -14,9 +14,9 @@
|
||||
#pragma once
|
||||
|
||||
#include "formats/hti_tape.h"
|
||||
#include "machine/hp_dc100_tape.h"
|
||||
|
||||
class hp_taco_device : public device_t ,
|
||||
public device_image_interface
|
||||
class hp_taco_device : public device_t
|
||||
{
|
||||
public:
|
||||
// construction/destruction
|
||||
@ -35,113 +35,86 @@ public:
|
||||
DECLARE_READ_LINE_MEMBER(flg_r);
|
||||
DECLARE_READ_LINE_MEMBER(sts_r);
|
||||
|
||||
// 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;
|
||||
DECLARE_WRITE_LINE_MEMBER(cart_out_w);
|
||||
DECLARE_WRITE_LINE_MEMBER(hole_w);
|
||||
DECLARE_WRITE_LINE_MEMBER(tacho_tick_w);
|
||||
DECLARE_WRITE_LINE_MEMBER(motion_w);
|
||||
DECLARE_WRITE_LINE_MEMBER(rd_bit_w);
|
||||
DECLARE_READ_LINE_MEMBER(wr_bit_r);
|
||||
|
||||
protected:
|
||||
hp_taco_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock);
|
||||
|
||||
// device-level overrides
|
||||
virtual void device_add_mconfig(machine_config &config) override;
|
||||
virtual void device_start() override;
|
||||
virtual void device_stop() override;
|
||||
virtual void device_reset() override;
|
||||
virtual void device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr) override;
|
||||
|
||||
private:
|
||||
required_device<hp_dc100_tape_device> m_tape;
|
||||
|
||||
devcb_write_line m_irq_handler;
|
||||
devcb_write_line m_flg_handler;
|
||||
devcb_write_line m_sts_handler;
|
||||
|
||||
// Registers
|
||||
uint16_t m_data_reg;
|
||||
bool m_data_reg_full;
|
||||
uint16_t m_cmd_reg;
|
||||
uint16_t m_status_reg;
|
||||
uint16_t m_tach_reg;
|
||||
hti_format_t::tape_pos_t m_tach_reg_ref;
|
||||
bool m_tach_reg_frozen;
|
||||
uint16_t m_checksum_reg;
|
||||
bool m_clear_checksum_reg;
|
||||
uint16_t m_timing_reg;
|
||||
uint16_t m_threshold_reg;
|
||||
|
||||
// State
|
||||
bool m_irq;
|
||||
bool m_flg;
|
||||
bool m_sts;
|
||||
bool m_error;
|
||||
bool m_gap_in_read;
|
||||
|
||||
// Command FSM state
|
||||
typedef enum {
|
||||
CMD_IDLE,
|
||||
CMD_INVERTING,
|
||||
CMD_PH0,
|
||||
CMD_PH1,
|
||||
CMD_PH2,
|
||||
CMD_END,
|
||||
CMD_STOPPING
|
||||
} cmd_state_t;
|
||||
cmd_state_t m_cmd_state;
|
||||
|
||||
// Tape position & motion
|
||||
hti_format_t::tape_pos_t m_tape_pos;
|
||||
attotime m_start_time; // Tape moving if != never
|
||||
bool m_tape_fwd;
|
||||
bool m_tape_fast;
|
||||
|
||||
// Timers
|
||||
emu_timer *m_tape_timer;
|
||||
emu_timer *m_hole_timer;
|
||||
emu_timer *m_timeout_timer;
|
||||
|
||||
// Content of tape
|
||||
hti_format_t m_image;
|
||||
bool m_image_dirty;
|
||||
emu_timer *m_gap_timer;
|
||||
emu_timer *m_evd_timer;
|
||||
emu_timer *m_error_timer;
|
||||
|
||||
// Reading & writing
|
||||
bool m_tape_wr;
|
||||
hti_format_t::tape_pos_t m_rw_pos;
|
||||
uint16_t m_next_word;
|
||||
hti_format_t::track_iterator_t m_rd_it;
|
||||
bool m_rd_it_valid;
|
||||
uint16_t m_working_reg;
|
||||
unsigned m_bit_idx;
|
||||
|
||||
// Gap detection
|
||||
hti_format_t::tape_pos_t m_gap_detect_start;
|
||||
|
||||
void clear_state(void);
|
||||
void clear_state();
|
||||
void irq_w(bool state);
|
||||
void set_error(bool state);
|
||||
bool is_braking(void) const;
|
||||
unsigned speed_to_tick_freq(void) const;
|
||||
hti_format_t::tape_pos_t current_tape_pos(void) const;
|
||||
void update_tape_pos(void);
|
||||
void update_tach_reg(void);
|
||||
void freeze_tach_reg(bool freeze);
|
||||
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_stopping_pos(void) const;
|
||||
bool start_tape_cmd(uint16_t cmd_reg , uint16_t must_be_1 , uint16_t must_be_0);
|
||||
void stop_tape(void);
|
||||
unsigned current_track(void);
|
||||
attotime fetch_next_wr_word(void);
|
||||
attotime time_to_rd_next_word(hti_format_t::tape_pos_t& word_rd_pos);
|
||||
hti_format_t::tape_pos_t min_gap_size(void) const;
|
||||
void sts_w(bool state);
|
||||
void set_error(bool error , bool gap_in_read);
|
||||
hti_format_t::tape_pos_t min_gap_size() const;
|
||||
void set_gap_timer();
|
||||
void set_evd_timer();
|
||||
void set_tape_present(bool present);
|
||||
attotime time_to_next_hole(void) const;
|
||||
attotime time_to_tach_pulses(void) const;
|
||||
void terminate_cmd_now(void);
|
||||
void set_data_timeout(bool long_timeout);
|
||||
void cmd_fsm(void);
|
||||
void send_go();
|
||||
void send_stop();
|
||||
void end_cmd();
|
||||
void irq_and_end();
|
||||
bool is_at_slow_speed() const;
|
||||
void start_rd();
|
||||
void start_wr();
|
||||
void update_checksum(uint16_t data);
|
||||
void cmd_fsm();
|
||||
static uint8_t get_cmd(uint16_t cmd_reg);
|
||||
static bool is_cmd_rd_wr(uint16_t cmd_reg);
|
||||
static bool is_cmd_rd(uint16_t cmd_reg);
|
||||
static bool is_cmd_wr(uint16_t cmd_reg);
|
||||
static bool is_double_hole_cmd(uint16_t cmd_reg);
|
||||
void start_cmd_exec(uint16_t new_cmd_reg);
|
||||
image_init_result internal_load(bool is_create);
|
||||
};
|
||||
|
||||
// device type definition
|
||||
|
@ -11,7 +11,8 @@
|
||||
#include "imageutl.h"
|
||||
|
||||
|
||||
static constexpr uint32_t FILE_MAGIC = 0x5441434f; // Magic value at start of image file: "TACO"
|
||||
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"
|
||||
|
||||
// *** Position of tape holes ***
|
||||
// At beginning of tape:
|
||||
@ -40,6 +41,7 @@ static const hti_format_t::tape_pos_t tape_holes[] = {
|
||||
};
|
||||
|
||||
hti_format_t::hti_format_t()
|
||||
: m_bits_per_word(16)
|
||||
{
|
||||
clear_tape();
|
||||
}
|
||||
@ -49,14 +51,15 @@ bool hti_format_t::load_tape(io_generic *io)
|
||||
uint8_t tmp[ 4 ];
|
||||
|
||||
io_generic_read(io, tmp, 0, 4);
|
||||
if (pick_integer_be(tmp , 0 , 4) != FILE_MAGIC) {
|
||||
auto magic = pick_integer_be(tmp , 0 , 4);
|
||||
if (magic != FILE_MAGIC && magic != OLD_FILE_MAGIC) {
|
||||
return false;
|
||||
}
|
||||
|
||||
uint64_t offset = 4;
|
||||
|
||||
for (tape_track_t& track : m_tracks) {
|
||||
if (!load_track(io , offset , track)) {
|
||||
if (!load_track(io , offset , track , magic == OLD_FILE_MAGIC)) {
|
||||
clear_tape();
|
||||
return false;
|
||||
}
|
||||
@ -114,7 +117,7 @@ hti_format_t::tape_pos_t hti_format_t::word_length(tape_word_t w)
|
||||
|
||||
zeros = 16 - ones;
|
||||
|
||||
return zeros * ZERO_BIT_LEN + (ones + 1) * ONE_BIT_LEN;
|
||||
return zeros * ZERO_BIT_LEN + ones * ONE_BIT_LEN;
|
||||
}
|
||||
|
||||
hti_format_t::tape_pos_t hti_format_t::farthest_end(const track_iterator_t& it , bool forward)
|
||||
@ -187,7 +190,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 + ONE_BIT_LEN)) {
|
||||
if (forward && it_high != track.end() && (it_high->first - end_pos) >= (ZERO_BIT_LEN * 16)) {
|
||||
track.insert(it_high, std::make_pair(end_pos, 0));
|
||||
it_high--;
|
||||
}
|
||||
@ -264,6 +267,50 @@ hti_format_t::adv_res_t hti_format_t::adv_it(unsigned track_no , bool forward ,
|
||||
}
|
||||
}
|
||||
|
||||
bool hti_format_t::sync_with_record(unsigned track_no , track_iterator_t& it , unsigned& bit_idx)
|
||||
{
|
||||
while ((it->second & (1U << bit_idx)) == 0) {
|
||||
if (bit_idx) {
|
||||
bit_idx--;
|
||||
} else {
|
||||
bit_idx = 15;
|
||||
auto res = adv_it(track_no, true, it);
|
||||
if (res != ADV_CONT_DATA) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (bit_idx) {
|
||||
bit_idx--;
|
||||
} else {
|
||||
bit_idx = 15;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
hti_format_t::adv_res_t hti_format_t::next_word(unsigned track_no , track_iterator_t& it , unsigned& bit_idx , tape_word_t& word)
|
||||
{
|
||||
if (bit_idx == 15) {
|
||||
auto res = adv_it(track_no, true, it);
|
||||
if (res == ADV_NO_MORE_DATA) {
|
||||
return res;
|
||||
}
|
||||
word = it->second;
|
||||
return res;
|
||||
} else {
|
||||
word = it->second << (15 - bit_idx);
|
||||
auto res = adv_it(track_no, true, it);
|
||||
if (res == ADV_DISCONT_DATA) {
|
||||
bit_idx = 15;
|
||||
it--;
|
||||
} else if (res == ADV_CONT_DATA) {
|
||||
word |= (it->second >> (bit_idx + 1));
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
bool hti_format_t::next_gap(unsigned track_no , tape_pos_t& pos , bool forward , tape_pos_t min_gap)
|
||||
{
|
||||
tape_track_t::iterator it;
|
||||
@ -323,10 +370,12 @@ bool hti_format_t::next_gap(unsigned track_no , tape_pos_t& pos , bool forward ,
|
||||
return n_gaps == 0;
|
||||
}
|
||||
|
||||
bool hti_format_t::load_track(io_generic *io , uint64_t& offset , tape_track_t& track)
|
||||
bool hti_format_t::load_track(io_generic *io , uint64_t& offset , tape_track_t& track , bool old_format)
|
||||
{
|
||||
uint8_t tmp[ 4 ];
|
||||
uint32_t tmp32;
|
||||
tape_pos_t delta_pos = 0;
|
||||
tape_pos_t last_word_end = 0;
|
||||
|
||||
track.clear();
|
||||
|
||||
@ -350,7 +399,10 @@ bool hti_format_t::load_track(io_generic *io , uint64_t& offset , tape_track_t&
|
||||
|
||||
tmp32 = pick_integer_le(tmp, 0, 4);
|
||||
|
||||
tape_pos_t pos = (tape_pos_t)tmp32;
|
||||
tape_pos_t pos = (tape_pos_t)tmp32 + delta_pos;
|
||||
|
||||
tape_word_t word_accum = 0;
|
||||
unsigned bits_in_accum = 0;
|
||||
|
||||
for (unsigned i = 0; i < n_words; i++) {
|
||||
uint16_t tmp16;
|
||||
@ -359,8 +411,56 @@ bool hti_format_t::load_track(io_generic *io , uint64_t& offset , tape_track_t&
|
||||
offset += 2;
|
||||
tmp16 = pick_integer_le(tmp, 0, 2);
|
||||
|
||||
track.insert(std::make_pair(pos , tmp16));
|
||||
pos += word_length(tmp16);
|
||||
if (!old_format) {
|
||||
track.insert(std::make_pair(pos , tmp16));
|
||||
pos += word_length(tmp16);
|
||||
} else if (m_bits_per_word == 16) {
|
||||
// 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
|
||||
// and don't add the 17th bit.
|
||||
if (i == 0 && tmp16 == 0 && (pos - last_word_end) > 16384) {
|
||||
// This mysterious heuristic is meant to turn the first
|
||||
// word of a record into a proper preamble word (from 0 to 1)
|
||||
// provided this is actually at the beginning of a new record
|
||||
// (enough distance from end of last record)
|
||||
tmp16 = 1;
|
||||
}
|
||||
track.insert(std::make_pair(pos, tmp16));
|
||||
pos += word_length(tmp16);
|
||||
last_word_end = pos;
|
||||
delta_pos -= ONE_BIT_LEN;
|
||||
} else {
|
||||
// Convert HP9825 old format
|
||||
// In moving from old to new format we make the 17th bit at the
|
||||
// end of each word explicit
|
||||
word_accum |= (tmp16 >> bits_in_accum);
|
||||
// Avoid storing overlapping words
|
||||
if (pos >= last_word_end) {
|
||||
track.insert(std::make_pair(pos, word_accum));
|
||||
}
|
||||
pos += word_length(word_accum);
|
||||
last_word_end = pos;
|
||||
if (bits_in_accum == 0) {
|
||||
word_accum = 0;
|
||||
} else {
|
||||
word_accum = tmp16 << (16 - bits_in_accum);
|
||||
}
|
||||
word_accum |= (1U << (15 - bits_in_accum));
|
||||
if (++bits_in_accum >= 16) {
|
||||
track.insert(std::make_pair(pos, word_accum));
|
||||
pos += word_length(word_accum);
|
||||
last_word_end = pos;
|
||||
word_accum = 0;
|
||||
bits_in_accum = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
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;
|
||||
delta_pos += shift;
|
||||
last_word_end = pos + word_length(word_accum);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -4,8 +4,8 @@
|
||||
|
||||
"HTI" format
|
||||
|
||||
Format of images of DC-100 tape cassettes as used in HP 9845
|
||||
and HP 85 systems.
|
||||
Format of images of DC-100 tape cassettes as used in HP 9825,
|
||||
HP 9845 and HP 85 systems.
|
||||
|
||||
*********************************************************************/
|
||||
#ifndef MAME_FORMATS_HTI_TAPE_H
|
||||
@ -49,10 +49,16 @@ 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; }
|
||||
|
||||
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; }
|
||||
|
||||
// Return physical length of a 16-bit word on tape
|
||||
static tape_pos_t word_length(tape_word_t w);
|
||||
|
||||
@ -84,14 +90,22 @@ public:
|
||||
// Advance an iterator to next word of data
|
||||
adv_res_t adv_it(unsigned track_no , bool forward , track_iterator_t& it);
|
||||
|
||||
// Sync with the preamble of a record
|
||||
bool sync_with_record(unsigned track_no , track_iterator_t& it , unsigned& bit_idx);
|
||||
|
||||
// Get a data word from record, after syncing
|
||||
adv_res_t next_word(unsigned track_no , track_iterator_t& it , unsigned& bit_idx , tape_word_t& word);
|
||||
|
||||
// Scan for beginning of next gap in a given direction
|
||||
bool next_gap(unsigned track_no , tape_pos_t& pos , bool forward , tape_pos_t min_gap);
|
||||
|
||||
private:
|
||||
// Content of tape tracks
|
||||
tape_track_t m_tracks[ 2 ];
|
||||
// Bits per word in old format
|
||||
unsigned m_bits_per_word;
|
||||
|
||||
static bool load_track(io_generic *io , uint64_t& offset , tape_track_t& track);
|
||||
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);
|
||||
|
@ -16,11 +16,10 @@
|
||||
#include "logmacro.h"
|
||||
#define LOG_REG_MASK (LOG_GENERAL << 1)
|
||||
#define LOG_REG(...) LOGMASKED(LOG_REG_MASK, __VA_ARGS__)
|
||||
#define LOG_TMR_MASK (LOG_REG_MASK << 1)
|
||||
#define LOG_TMR(...) LOGMASKED(LOG_TMR_MASK, __VA_ARGS__)
|
||||
#define LOG_DBG_MASK (LOG_TMR_MASK << 1)
|
||||
#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
|
||||
@ -44,11 +43,9 @@ namespace {
|
||||
// Constants
|
||||
constexpr double FAST_SPEED = 90.0; // Fast speed: 90 ips
|
||||
constexpr double SLOW_SPEED = 22.0; // Slow speed: 22 ips
|
||||
constexpr double MIN_RD_SPEED = 22.0; // Minimum speed to read data off the tape
|
||||
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 double INVERSION_MARGIN = 1e-5; // Margin to ensure speed is away from 0 when motion is inverted (10 µs)
|
||||
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
|
||||
@ -74,33 +71,36 @@ enum : unsigned {
|
||||
STAT_REG_EOT_BIT = 0 // End of tape (1)
|
||||
};
|
||||
|
||||
// Timers
|
||||
enum {
|
||||
BIT_TMR_ID,
|
||||
TACHO_TMR_ID,
|
||||
HOLE_TMR_ID,
|
||||
INV_TMR_ID
|
||||
};
|
||||
|
||||
// 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)
|
||||
, device_image_interface(mconfig , *this)
|
||||
, 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")
|
||||
, m_image()
|
||||
, m_image_dirty(false)
|
||||
{
|
||||
}
|
||||
|
||||
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));
|
||||
@ -124,11 +124,6 @@ void hp9825_tape_device::device_start()
|
||||
m_dmar_handler.resolve_safe();
|
||||
m_led_handler.resolve_safe();
|
||||
|
||||
m_bit_timer = timer_alloc(BIT_TMR_ID);
|
||||
m_tacho_timer = timer_alloc(TACHO_TMR_ID);
|
||||
m_hole_timer = timer_alloc(HOLE_TMR_ID);
|
||||
m_inv_timer = timer_alloc(INV_TMR_ID);
|
||||
|
||||
save_item(NAME(m_cmd_reg));
|
||||
save_item(NAME(m_stat_reg));
|
||||
save_item(NAME(m_flg));
|
||||
@ -140,24 +135,10 @@ void hp9825_tape_device::device_start()
|
||||
save_item(NAME(m_dma_req));
|
||||
save_item(NAME(m_in_gap));
|
||||
save_item(NAME(m_no_go));
|
||||
save_item(NAME(m_present));
|
||||
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));
|
||||
save_item(NAME(m_image_dirty));
|
||||
save_item(NAME(m_tape_pos));
|
||||
save_item(NAME(m_next_bit_pos));
|
||||
save_item(NAME(m_next_tacho_pos));
|
||||
save_item(NAME(m_next_hole_pos));
|
||||
save_item(NAME(m_speed));
|
||||
save_item(NAME(m_start_time));
|
||||
save_item(NAME(m_accelerating));
|
||||
save_item(NAME(m_rw_stat));
|
||||
save_item(NAME(m_rd_it_valid));
|
||||
save_item(NAME(m_rw_word));
|
||||
save_item(NAME(m_bit_idx));
|
||||
save_item(NAME(m_gap_start));
|
||||
}
|
||||
|
||||
void hp9825_tape_device::device_reset()
|
||||
@ -181,13 +162,6 @@ void hp9825_tape_device::clear_state()
|
||||
m_no_go = false;
|
||||
m_valid_bits = false;
|
||||
m_trans_cnt = 0;
|
||||
m_tape_pos = 80 * hti_format_t::ONE_INCH_POS;
|
||||
m_speed = 0.0;
|
||||
m_start_time = attotime::never;
|
||||
m_accelerating = false;
|
||||
m_rw_stat = RW_IDLE;
|
||||
m_rd_it_valid = false;
|
||||
m_gap_start = hti_format_t::NULL_TAPE_POS;
|
||||
|
||||
m_short_gap_timer->b_w(0);
|
||||
m_long_gap_timer->a_w(1);
|
||||
@ -197,152 +171,6 @@ void hp9825_tape_device::clear_state()
|
||||
m_sts_handler(true);
|
||||
m_dmar_handler(false);
|
||||
m_led_handler(false);
|
||||
|
||||
m_bit_timer->reset();
|
||||
m_tacho_timer->reset();
|
||||
m_hole_timer->reset();
|
||||
m_inv_timer->reset();
|
||||
|
||||
set_tape_present(false);
|
||||
set_tape_present(is_loaded());
|
||||
}
|
||||
|
||||
void hp9825_tape_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr)
|
||||
{
|
||||
LOG_TMR("%.6f TMR %d s=%.3f p=%d a=%d\n" , machine().time().as_double() , id , m_speed , m_tape_pos , m_accelerating);
|
||||
update_speed_pos();
|
||||
|
||||
switch (id) {
|
||||
case BIT_TMR_ID:
|
||||
m_tape_pos = m_next_bit_pos;
|
||||
if (m_rw_stat == RW_READING) {
|
||||
// Reading
|
||||
// Tape pos here is aligned with the beginning of bit cell. It'd be more correct
|
||||
// to align with end of cell, though. This solution is a lot simpler and
|
||||
// it's basically harmless.
|
||||
|
||||
// 17th bit is sync (always 1)
|
||||
bool bit = m_bit_idx == 16 ? true : BIT(m_rd_it->second , 15 - m_bit_idx);
|
||||
rd_bit(bit);
|
||||
if (is_moving_fwd()) {
|
||||
m_bit_idx++;
|
||||
if (m_bit_idx >= 17) {
|
||||
m_rd_it_valid = m_image.adv_it(current_track() , true , m_rd_it) != hti_format_t::ADV_NO_MORE_DATA;
|
||||
load_rd_word();
|
||||
} else {
|
||||
time_to_distance(bit_size(bit), m_next_bit_pos, m_bit_timer);
|
||||
}
|
||||
} else {
|
||||
if (m_bit_idx > 0) {
|
||||
m_bit_idx--;
|
||||
time_to_distance(-bit_size(bit), m_next_bit_pos, m_bit_timer);
|
||||
} else {
|
||||
m_rd_it_valid = m_image.adv_it(current_track() , false , m_rd_it) != hti_format_t::ADV_NO_MORE_DATA;
|
||||
load_rd_word();
|
||||
}
|
||||
}
|
||||
} else if (m_rw_stat == RW_WRITING) {
|
||||
// Writing
|
||||
// Tape pos is aligned with beginning of bit cell
|
||||
bool bit = m_data_in;
|
||||
if (BIT(m_cmd_reg , CMD_REG_FLG_SEL_BIT)) {
|
||||
set_flg(true);
|
||||
}
|
||||
wr_bit(bit);
|
||||
time_to_distance(bit_size(bit), m_next_bit_pos, m_bit_timer);
|
||||
}
|
||||
break;
|
||||
|
||||
case TACHO_TMR_ID:
|
||||
m_tape_pos = m_next_tacho_pos;
|
||||
if (!BIT(m_cmd_reg , CMD_REG_FLG_SEL_BIT)) {
|
||||
set_flg(true);
|
||||
}
|
||||
adjust_tacho_timer();
|
||||
break;
|
||||
|
||||
case HOLE_TMR_ID:
|
||||
m_tape_pos = m_next_hole_pos;
|
||||
BIT_SET(m_stat_reg , STAT_REG_EOT_BIT);
|
||||
update_sts();
|
||||
adjust_hole_timer();
|
||||
break;
|
||||
|
||||
case INV_TMR_ID:
|
||||
// In itself it does nothing (all work is in update_speed_pos)
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
LOG_TMR("%.6f TMR %d s=%.3f p=%d a=%d\n" , machine().time().as_double() , id , m_speed , m_tape_pos , m_accelerating);
|
||||
}
|
||||
|
||||
image_init_result hp9825_tape_device::internal_load(bool is_create)
|
||||
{
|
||||
LOG("load %d\n" , 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)) {
|
||||
LOG("load failed\n");
|
||||
seterror(IMAGE_ERROR_INVALIDIMAGE , "Wrong format");
|
||||
set_tape_present(false);
|
||||
return image_init_result::FAIL;
|
||||
}
|
||||
LOG("load OK\n");
|
||||
|
||||
m_image_dirty = false;
|
||||
|
||||
set_tape_present(true);
|
||||
return image_init_result::PASS;
|
||||
}
|
||||
|
||||
image_init_result hp9825_tape_device::call_load()
|
||||
{
|
||||
return internal_load(false);
|
||||
}
|
||||
|
||||
image_init_result hp9825_tape_device::call_create(int format_type, util::option_resolution *format_options)
|
||||
{
|
||||
return internal_load(true);
|
||||
}
|
||||
|
||||
void hp9825_tape_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_tape_present(false);
|
||||
}
|
||||
|
||||
std::string hp9825_tape_device::call_display()
|
||||
{
|
||||
// TODO:
|
||||
return std::string();
|
||||
}
|
||||
|
||||
const char *hp9825_tape_device::file_extensions() const
|
||||
{
|
||||
return "hti";
|
||||
}
|
||||
|
||||
READ16_MEMBER(hp9825_tape_device::tape_r)
|
||||
@ -398,16 +226,16 @@ WRITE16_MEMBER(hp9825_tape_device::tape_w)
|
||||
case 1:
|
||||
// R5: write command
|
||||
{
|
||||
double old_set_point = get_speed_set_point();
|
||||
auto old_cmd_reg = m_cmd_reg;
|
||||
m_cmd_reg = data;
|
||||
check_for_speed_change(old_set_point);
|
||||
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;
|
||||
@ -418,7 +246,7 @@ WRITE16_MEMBER(hp9825_tape_device::tape_w)
|
||||
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
|
||||
update_speed_pos();
|
||||
m_tape->update_speed_pos();
|
||||
start_rd_wr();
|
||||
}
|
||||
}
|
||||
@ -436,7 +264,7 @@ WRITE16_MEMBER(hp9825_tape_device::tape_w)
|
||||
|
||||
case 3:
|
||||
// R7: reset status
|
||||
if (m_present) {
|
||||
if (!m_tape->cart_out_r()) {
|
||||
BIT_CLR(m_stat_reg , STAT_REG_COUT_BIT);
|
||||
update_sts();
|
||||
}
|
||||
@ -485,6 +313,92 @@ void hp9825_tape_device::set_flg(bool state)
|
||||
}
|
||||
}
|
||||
|
||||
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:
|
||||
@ -494,7 +408,6 @@ void hp9825_tape_device::update_sts()
|
||||
// STAT_REG_COUT_BIT
|
||||
// m_search_complete
|
||||
// m_in_gap
|
||||
auto prev_set_point = get_speed_set_point();
|
||||
auto prev_exception = m_exception;
|
||||
|
||||
m_exception =
|
||||
@ -503,7 +416,7 @@ void hp9825_tape_device::update_sts()
|
||||
m_search_complete;
|
||||
|
||||
if (prev_exception != m_exception) {
|
||||
check_for_speed_change(prev_set_point);
|
||||
check_for_speed_change();
|
||||
}
|
||||
|
||||
m_no_go = m_exception && !BIT(m_cmd_reg , CMD_REG_MOTOR_BIT);
|
||||
@ -526,23 +439,6 @@ void hp9825_tape_device::update_dmar()
|
||||
m_dmar_handler(m_dma_req && !BIT(m_cmd_reg , CMD_REG_DMA_EN_BIT));
|
||||
}
|
||||
|
||||
void hp9825_tape_device::set_tape_present(bool present)
|
||||
{
|
||||
m_present = present;
|
||||
if (present) {
|
||||
if (is_readonly()) {
|
||||
BIT_SET(m_stat_reg, STAT_REG_WPR_BIT);
|
||||
} else {
|
||||
BIT_CLR(m_stat_reg, STAT_REG_WPR_BIT);
|
||||
}
|
||||
// STAT_REG_COUT_BIT is cleared by a write to R7
|
||||
} else {
|
||||
BIT_SET(m_stat_reg, STAT_REG_COUT_BIT);
|
||||
BIT_SET(m_stat_reg, STAT_REG_WPR_BIT);
|
||||
update_sts();
|
||||
}
|
||||
}
|
||||
|
||||
bool hp9825_tape_device::is_moving_fwd() const
|
||||
{
|
||||
return BIT(m_cmd_reg , CMD_REG_DIR_BIT);
|
||||
@ -553,412 +449,37 @@ bool hp9825_tape_device::is_speed_fast() const
|
||||
return !BIT(m_cmd_reg , CMD_REG_SPEED_BIT);
|
||||
}
|
||||
|
||||
bool hp9825_tape_device::is_actual_dir_fwd() const
|
||||
void hp9825_tape_device::check_for_speed_change()
|
||||
{
|
||||
// Actual direction can be different from commanded direction in accelerated phases (e.g. when tape
|
||||
// direction is turned around)
|
||||
return m_speed == 0.0 ? is_moving_fwd() : m_speed > 0.0;
|
||||
}
|
||||
hp_dc100_tape_device::tape_speed_t new_speed;
|
||||
|
||||
double hp9825_tape_device::get_speed_set_point() const
|
||||
{
|
||||
if (m_exception || BIT(m_cmd_reg , CMD_REG_MOTOR_BIT)) {
|
||||
// Stop
|
||||
return 0.0;
|
||||
new_speed = hp_dc100_tape_device::SP_STOP;
|
||||
} else {
|
||||
double c;
|
||||
if (is_speed_fast()) {
|
||||
// HS
|
||||
c = FAST_SPEED;
|
||||
} else {
|
||||
// LS
|
||||
c = SLOW_SPEED;
|
||||
}
|
||||
if (!is_moving_fwd()) {
|
||||
// Reverse
|
||||
c = -c;
|
||||
}
|
||||
return c;
|
||||
new_speed = is_speed_fast() ? hp_dc100_tape_device::SP_FAST :
|
||||
hp_dc100_tape_device::SP_SLOW;
|
||||
}
|
||||
}
|
||||
|
||||
void hp9825_tape_device::start_tape()
|
||||
{
|
||||
LOG_DBG("Tape started %.6f p=%d\n" , machine().time().as_double() , m_tape_pos);
|
||||
m_start_time = machine().time();
|
||||
m_accelerating = true;
|
||||
m_speed = 0;
|
||||
}
|
||||
bool changed = m_tape->set_speed_setpoint(new_speed , is_moving_fwd());
|
||||
|
||||
void hp9825_tape_device::stop_tape()
|
||||
{
|
||||
LOG_DBG("Tape stops %.6f p=%d\n" , machine().time().as_double() , m_tape_pos);
|
||||
m_start_time = attotime::never;
|
||||
m_accelerating = false;
|
||||
m_speed = 0;
|
||||
m_tacho_timer->reset();
|
||||
m_hole_timer->reset();
|
||||
m_inv_timer->reset();
|
||||
stop_rd_wr();
|
||||
}
|
||||
|
||||
void hp9825_tape_device::check_for_speed_change(double prev_set_point)
|
||||
{
|
||||
double set_point = get_speed_set_point();
|
||||
if (prev_set_point != set_point) {
|
||||
update_speed_pos();
|
||||
LOG_DBG("Speed SP changed %f->%f %.6f p=%d\n" , prev_set_point , set_point , machine().time().as_double() , m_tape_pos);
|
||||
// Speed set point changed, accelerate/decelerate
|
||||
m_accelerating = true;
|
||||
if (m_start_time.is_never()) {
|
||||
// Tape starting now
|
||||
start_tape();
|
||||
}
|
||||
if (changed) {
|
||||
start_rd_wr(true);
|
||||
adjust_tacho_timer();
|
||||
adjust_hole_timer();
|
||||
set_inv_timer();
|
||||
}
|
||||
}
|
||||
|
||||
void hp9825_tape_device::update_speed_pos()
|
||||
{
|
||||
if (m_start_time.is_never()) {
|
||||
// Tape stopped
|
||||
return;
|
||||
}
|
||||
|
||||
attotime delta_time{machine().time() - m_start_time};
|
||||
if (delta_time.is_zero()) {
|
||||
return;
|
||||
}
|
||||
m_start_time = machine().time();
|
||||
double delta_time_double = delta_time.as_double();
|
||||
|
||||
// Space in const A phase
|
||||
double space_const_a;
|
||||
// Time in const V phase
|
||||
double time_const_v;
|
||||
// Do R/W start/stop
|
||||
bool rw_start_stop = false;
|
||||
|
||||
if (m_accelerating) {
|
||||
double set_point = get_speed_set_point();
|
||||
double time_to_const_v = fabs(set_point - m_speed) / ACCELERATION;
|
||||
double acceleration = set_point > m_speed ? ACCELERATION : -ACCELERATION;
|
||||
if (delta_time_double < time_to_const_v) {
|
||||
space_const_a = const_a_space(acceleration , delta_time_double);
|
||||
auto prev_speed = m_speed;
|
||||
m_speed += delta_time_double * acceleration;
|
||||
rw_start_stop = (fabs(prev_speed) >= MIN_RD_SPEED) != (fabs(m_speed) >= MIN_RD_SPEED);
|
||||
if (prev_speed * m_speed < 0.0) {
|
||||
// Direction inverted (speed sign flipped)
|
||||
LOG_DBG("Dir inverted s=%f\n" , m_speed);
|
||||
adjust_tacho_timer();
|
||||
adjust_hole_timer();
|
||||
}
|
||||
set_inv_timer();
|
||||
time_const_v = 0.0;
|
||||
} else {
|
||||
space_const_a = const_a_space(acceleration , time_to_const_v);
|
||||
time_const_v = delta_time_double - time_to_const_v;
|
||||
LOG_DBG("Acceleration ends\n");
|
||||
m_accelerating = false;
|
||||
m_speed = set_point;
|
||||
m_inv_timer->reset();
|
||||
if (m_speed == 0.0) {
|
||||
// Tape stops
|
||||
stop_tape();
|
||||
} else {
|
||||
rw_start_stop = true;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
space_const_a = 0.0;
|
||||
time_const_v = delta_time_double;
|
||||
}
|
||||
|
||||
// Update MVG bit
|
||||
if (fabs(m_speed) >= MOVING_THRESHOLD) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
hti_format_t::tape_pos_t delta_pos = (hti_format_t::tape_pos_t)((space_const_a + m_speed * time_const_v) * hti_format_t::ONE_INCH_POS);
|
||||
LOG_DBG("dp=%d\n" , delta_pos);
|
||||
if (!hti_format_t::pos_offset(m_tape_pos , true , delta_pos)) {
|
||||
LOG("Tape unspooled!\n");
|
||||
}
|
||||
|
||||
if (rw_start_stop) {
|
||||
start_rd_wr();
|
||||
}
|
||||
}
|
||||
|
||||
void hp9825_tape_device::set_inv_timer()
|
||||
{
|
||||
// Set inversion timer to go off at the moment the tape inverts its motion
|
||||
// (i.e. when m_speed crosses 0)
|
||||
if (m_speed == 0.0 || (get_speed_set_point() * m_speed) > 0.0) {
|
||||
LOG_DBG("Inv tmr reset\n");
|
||||
// No inversion in sight
|
||||
m_inv_timer->reset();
|
||||
} else {
|
||||
// INVERSION_MARGIN is added to ensure that m_speed has already cleared the
|
||||
// 0-crossing when inv timer goes off
|
||||
LOG_DBG("Inv tmr set %.6f %f\n" , machine().time().as_double() , m_speed);
|
||||
m_inv_timer->adjust(attotime::from_double(fabs(m_speed) / ACCELERATION + INVERSION_MARGIN));
|
||||
}
|
||||
}
|
||||
|
||||
void hp9825_tape_device::time_to_distance(hti_format_t::tape_pos_t distance , hti_format_t::tape_pos_t& target_pos , emu_timer *target_timer) const
|
||||
{
|
||||
if (m_start_time.is_never()) {
|
||||
// If tape is stopped we'll never get there..
|
||||
target_timer->reset();
|
||||
return;
|
||||
}
|
||||
|
||||
target_pos = m_tape_pos;
|
||||
if (!hti_format_t::pos_offset(target_pos , true , distance)) {
|
||||
// Beyond end of tape
|
||||
target_timer->reset();
|
||||
return;
|
||||
}
|
||||
|
||||
double space = double(distance) / hti_format_t::ONE_INCH_POS;
|
||||
double set_point = get_speed_set_point();
|
||||
double time_const_a;
|
||||
|
||||
if (m_accelerating) {
|
||||
// Time to reach constant V phase
|
||||
double time_to_const_v = fabs(set_point - m_speed) / ACCELERATION;
|
||||
// Signed acceleration
|
||||
double acceleration = set_point > m_speed ? ACCELERATION : -ACCELERATION;
|
||||
// Compute time to cover distance with constant acceleration
|
||||
// It's the smallest non-negative root of this quadratic equation:
|
||||
// 1/2*acceleration*t^2+m_speed*t=space
|
||||
double delta = m_speed * m_speed + 2 * acceleration * space;
|
||||
bool has_root = delta >= 0.0;
|
||||
double time_in_const_a = 0.0;
|
||||
if (has_root) {
|
||||
double time_in_const_a_pos = (sqrt(delta) - m_speed) / acceleration;
|
||||
double time_in_const_a_neg = -(sqrt(delta) + m_speed) / acceleration;
|
||||
LOG_DBG("TTD %.6f %.6f\n" , time_in_const_a_pos , time_in_const_a_neg);
|
||||
if (time_in_const_a_pos >= 0.0) {
|
||||
if (time_in_const_a_neg >= 0.0) {
|
||||
// pos + neg +
|
||||
time_in_const_a = std::min(time_in_const_a_pos , time_in_const_a_neg);
|
||||
} else {
|
||||
// pos + neg -
|
||||
time_in_const_a = time_in_const_a_pos;
|
||||
}
|
||||
} else {
|
||||
if (time_in_const_a_neg >= 0.0) {
|
||||
// pos - neg +
|
||||
time_in_const_a = time_in_const_a_neg;
|
||||
} else {
|
||||
// pos - neg -
|
||||
has_root = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
LOG_DBG("TTD %d %d %.6f %.6f %.6f\n" , distance , has_root , m_speed , time_to_const_v , time_in_const_a);
|
||||
if (has_root && time_in_const_a <= time_to_const_v) {
|
||||
// Entirely in the constant A phase
|
||||
time_const_a = time_in_const_a;
|
||||
space = 0.0;
|
||||
} else {
|
||||
// Partly in const A & partly in const V
|
||||
double space_in_const_a = const_a_space(acceleration , time_to_const_v);
|
||||
space -= space_in_const_a;
|
||||
time_const_a = time_to_const_v;
|
||||
}
|
||||
} else {
|
||||
// Entirely in const V
|
||||
time_const_a = 0.0;
|
||||
}
|
||||
|
||||
// Time in constant V
|
||||
double time_const_v;
|
||||
if (space != 0.0) {
|
||||
if (set_point == 0.0) {
|
||||
target_timer->reset();
|
||||
return;
|
||||
} else {
|
||||
time_const_v = space / set_point;
|
||||
if (time_const_v < 0.0) {
|
||||
target_timer->reset();
|
||||
return;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
time_const_v = 0.0;
|
||||
}
|
||||
LOG_DBG("TTD %.6f %.6f\n" , time_const_a , time_const_v);
|
||||
|
||||
target_timer->adjust(attotime::from_double(time_const_a + time_const_v));
|
||||
}
|
||||
|
||||
double hp9825_tape_device::const_a_space(double a , double t) const
|
||||
{
|
||||
// Space traveled in time 't' at constant acceleration 'a' starting with 'm_speed' speed
|
||||
return t * (m_speed + a / 2 * t);
|
||||
}
|
||||
|
||||
hti_format_t::tape_pos_t hp9825_tape_device::get_next_hole() const
|
||||
{
|
||||
return hti_format_t::next_hole(m_tape_pos , is_actual_dir_fwd()) - m_tape_pos;
|
||||
}
|
||||
|
||||
void hp9825_tape_device::adjust_tacho_timer()
|
||||
{
|
||||
hti_format_t::tape_pos_t tick = TACH_TICK_LENGTH;
|
||||
if (!is_actual_dir_fwd()) {
|
||||
tick = -tick;
|
||||
}
|
||||
time_to_distance(tick , m_next_tacho_pos , m_tacho_timer);
|
||||
}
|
||||
|
||||
void hp9825_tape_device::adjust_hole_timer()
|
||||
{
|
||||
time_to_distance(get_next_hole() , m_next_hole_pos , m_hole_timer);
|
||||
}
|
||||
|
||||
unsigned hp9825_tape_device::current_track() const
|
||||
{
|
||||
return !BIT(m_cmd_reg , CMD_REG_TRACK_SEL_BIT);
|
||||
}
|
||||
|
||||
constexpr hti_format_t::tape_pos_t hp9825_tape_device::bit_size(bool bit)
|
||||
{
|
||||
return bit ? hti_format_t::ONE_BIT_LEN : hti_format_t::ZERO_BIT_LEN;
|
||||
}
|
||||
|
||||
void hp9825_tape_device::start_rd_wr(bool recalc)
|
||||
{
|
||||
if (fabs(m_speed) >= MIN_RD_SPEED && BIT(m_cmd_reg , CMD_REG_WR_GATE_BIT)) {
|
||||
if (m_tape->is_above_threshold() && BIT(m_cmd_reg , CMD_REG_WR_GATE_BIT)) {
|
||||
// Reading
|
||||
if (m_rw_stat != RW_READING || recalc) {
|
||||
stop_rd_wr();
|
||||
LOG_DBG("Start RD @%d s=%f t=%u\n" , m_tape_pos , m_speed , current_track());
|
||||
m_rd_it_valid = m_image.next_data(current_track() , m_tape_pos , is_actual_dir_fwd() , false , m_rd_it);
|
||||
m_rw_stat = RW_READING;
|
||||
load_rd_word();
|
||||
}
|
||||
} else if (!m_accelerating && !BIT(m_cmd_reg , CMD_REG_WR_GATE_BIT) && BIT(m_cmd_reg , CMD_REG_THRESHOLD_BIT)) {
|
||||
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
|
||||
if (m_rw_stat != RW_WRITING) {
|
||||
stop_rd_wr();
|
||||
// Start WR, only LS FWD is allowed
|
||||
if (m_speed == SLOW_SPEED) {
|
||||
LOG_DBG("Start WR @%d\n" , m_tape_pos);
|
||||
// Looking for sync
|
||||
m_bit_idx = 17;
|
||||
m_rw_word = 0;
|
||||
time_to_distance(bit_size(true) , m_next_bit_pos , m_bit_timer);
|
||||
m_rw_stat = RW_WRITING;
|
||||
} else {
|
||||
LOG("Starting WR s=%f ???\n" , m_speed);
|
||||
}
|
||||
}
|
||||
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
|
||||
if (m_rw_stat != RW_WRITING_GAP) {
|
||||
stop_rd_wr();
|
||||
LOG_DBG("Start GAP @%d\n" , m_tape_pos);
|
||||
m_gap_start = m_tape_pos;
|
||||
m_rw_stat = RW_WRITING_GAP;
|
||||
}
|
||||
m_tape->set_op(hp_dc100_tape_device::OP_ERASE);
|
||||
} else {
|
||||
stop_rd_wr();
|
||||
m_tape->set_op(hp_dc100_tape_device::OP_IDLE);
|
||||
}
|
||||
}
|
||||
|
||||
void hp9825_tape_device::load_rd_word()
|
||||
{
|
||||
if (m_rd_it_valid) {
|
||||
if (is_moving_fwd()) {
|
||||
m_bit_idx = 0;
|
||||
} else {
|
||||
m_bit_idx = 16;
|
||||
}
|
||||
// This is actually the nearest end (dir is inverted)
|
||||
m_next_bit_pos = hti_format_t::farthest_end(m_rd_it , !is_actual_dir_fwd());
|
||||
LOG_DBG("Valid np=%d\n" , m_next_bit_pos);
|
||||
time_to_distance(m_next_bit_pos - m_tape_pos , m_next_bit_pos , m_bit_timer);
|
||||
} else {
|
||||
LOG_DBG("Invalid\n");
|
||||
stop_rd_wr();
|
||||
}
|
||||
}
|
||||
|
||||
void hp9825_tape_device::rd_bit(bool bit)
|
||||
{
|
||||
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 && bit;
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
void hp9825_tape_device::wr_bit(bool bit)
|
||||
{
|
||||
LOG_DBG("WR %d idx=%u\n" , bit , m_bit_idx);
|
||||
m_rw_word <<= 1;
|
||||
m_rw_word |= bit;
|
||||
|
||||
// A bit of heuristic here to achieve synchronization (i.e. find word boundary in bit stream)
|
||||
if (m_bit_idx >= 17 && m_bit_idx < (17 + 16)) {
|
||||
m_bit_idx++;
|
||||
} else if (m_bit_idx == (17 + 16) && (m_rw_word & 3) == 3) {
|
||||
m_bit_idx = 16;
|
||||
}
|
||||
if (m_bit_idx < 16) {
|
||||
m_bit_idx++;
|
||||
} else if (m_bit_idx == 16) {
|
||||
// Write word
|
||||
if (!BIT(m_rw_word , 0)) {
|
||||
LOG_DBG("Sync lost! w=%05x\n" , m_rw_word & 0x1ffff);
|
||||
} else {
|
||||
hti_format_t::tape_word_t w = (hti_format_t::tape_word_t)(m_rw_word >> 1);
|
||||
hti_format_t::tape_pos_t wr_pos = m_tape_pos - hti_format_t::word_length(w) + hti_format_t::ONE_BIT_LEN;
|
||||
LOG_DBG("Wr word %04x @%d\n" , w , wr_pos);
|
||||
m_image.write_word(current_track() , wr_pos , w , wr_pos);
|
||||
m_image_dirty = true;
|
||||
m_bit_idx = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void hp9825_tape_device::stop_rd_wr()
|
||||
{
|
||||
if (m_rw_stat == RW_WRITING_GAP) {
|
||||
LOG_DBG("Wr gap from %d to %d\n" , m_gap_start , m_tape_pos);
|
||||
m_image.write_gap(current_track() , m_gap_start , m_tape_pos);
|
||||
m_image_dirty = true;
|
||||
}
|
||||
m_bit_timer->reset();
|
||||
m_rw_stat = RW_IDLE;
|
||||
}
|
||||
|
||||
|
@ -14,9 +14,10 @@
|
||||
#pragma once
|
||||
|
||||
#include "formats/hti_tape.h"
|
||||
#include "machine/hp_dc100_tape.h"
|
||||
#include "machine/74123.h"
|
||||
|
||||
class hp9825_tape_device : public device_t , public device_image_interface
|
||||
class hp9825_tape_device : public device_t
|
||||
{
|
||||
public:
|
||||
// construction/destruction
|
||||
@ -26,20 +27,6 @@ public:
|
||||
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;
|
||||
|
||||
// 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;
|
||||
|
||||
DECLARE_READ16_MEMBER(tape_r);
|
||||
DECLARE_WRITE16_MEMBER(tape_w);
|
||||
@ -52,12 +39,20 @@ public:
|
||||
DECLARE_WRITE_LINE_MEMBER(short_gap_w);
|
||||
DECLARE_WRITE_LINE_MEMBER(long_gap_w);
|
||||
|
||||
DECLARE_WRITE_LINE_MEMBER(cart_out_w);
|
||||
DECLARE_WRITE_LINE_MEMBER(hole_w);
|
||||
DECLARE_WRITE_LINE_MEMBER(tacho_tick_w);
|
||||
DECLARE_WRITE_LINE_MEMBER(motion_w);
|
||||
DECLARE_WRITE_LINE_MEMBER(rd_bit_w);
|
||||
DECLARE_READ_LINE_MEMBER(wr_bit_r);
|
||||
|
||||
private:
|
||||
devcb_write_line m_flg_handler;
|
||||
devcb_write_line m_sts_handler;
|
||||
devcb_write_line m_dmar_handler;
|
||||
devcb_write_line m_led_handler;
|
||||
|
||||
required_device<hp_dc100_tape_device> m_tape;
|
||||
required_device<ttl74123_device> m_short_gap_timer; // U43a
|
||||
required_device<ttl74123_device> m_long_gap_timer; // U43b
|
||||
|
||||
@ -75,73 +70,19 @@ private:
|
||||
bool m_dma_req; // U9-9
|
||||
bool m_in_gap; // U39-4
|
||||
bool m_no_go; // U6-3
|
||||
bool m_present;
|
||||
bool m_valid_bits; // U39-5
|
||||
uint8_t m_trans_cnt; // U42
|
||||
bool m_short_gap_out; // U43-13
|
||||
bool m_long_gap_out; // U43-5
|
||||
|
||||
// Timers
|
||||
emu_timer *m_bit_timer;
|
||||
emu_timer *m_tacho_timer;
|
||||
emu_timer *m_hole_timer;
|
||||
emu_timer *m_inv_timer;
|
||||
|
||||
// Image of tape
|
||||
hti_format_t m_image;
|
||||
bool m_image_dirty;
|
||||
|
||||
// Tape motion
|
||||
hti_format_t::tape_pos_t m_tape_pos;
|
||||
hti_format_t::tape_pos_t m_next_bit_pos;
|
||||
hti_format_t::tape_pos_t m_next_tacho_pos;
|
||||
hti_format_t::tape_pos_t m_next_hole_pos;
|
||||
double m_speed;
|
||||
attotime m_start_time; // Tape moving if != never
|
||||
bool m_accelerating;
|
||||
|
||||
// R/W
|
||||
enum {
|
||||
RW_IDLE,
|
||||
RW_READING,
|
||||
RW_WRITING,
|
||||
RW_WRITING_GAP
|
||||
};
|
||||
|
||||
int m_rw_stat;
|
||||
hti_format_t::track_iterator_t m_rd_it;
|
||||
bool m_rd_it_valid;
|
||||
uint32_t m_rw_word; // Need 17 bits because of sync bit
|
||||
unsigned m_bit_idx; // 0 is MSB, 15 is LSB, 16 is sync bit, >16 means "looking for sync"
|
||||
hti_format_t::tape_pos_t m_gap_start;
|
||||
|
||||
void clear_state();
|
||||
image_init_result internal_load(bool is_create);
|
||||
void set_flg(bool state);
|
||||
void update_sts();
|
||||
void update_dmar();
|
||||
void set_tape_present(bool present);
|
||||
bool is_moving_fwd() const;
|
||||
bool is_speed_fast() const;
|
||||
bool is_actual_dir_fwd() const;
|
||||
double get_speed_set_point() const;
|
||||
void start_tape();
|
||||
void stop_tape();
|
||||
void check_for_speed_change(double prev_set_point);
|
||||
void update_speed_pos();
|
||||
void set_inv_timer();
|
||||
void time_to_distance(hti_format_t::tape_pos_t distance , hti_format_t::tape_pos_t& target_pos , emu_timer *target_timer) const;
|
||||
double const_a_space(double a , double t) const;
|
||||
hti_format_t::tape_pos_t get_next_hole() const;
|
||||
void adjust_tacho_timer();
|
||||
void adjust_hole_timer();
|
||||
unsigned current_track() const;
|
||||
static constexpr hti_format_t::tape_pos_t bit_size(bool bit);
|
||||
void check_for_speed_change();
|
||||
void start_rd_wr(bool recalc = false);
|
||||
void load_rd_word();
|
||||
void rd_bit(bool bit);
|
||||
void wr_bit(bool bit);
|
||||
void stop_rd_wr();
|
||||
};
|
||||
|
||||
// device type definition
|
||||
|
@ -43,8 +43,8 @@ static constexpr tape_pos_t FMT_IFG_SIZE = 2.5 * hti_format_t::ONE_INCH_POS;
|
||||
// Formatted size of IRGs: 1"
|
||||
static constexpr tape_pos_t FMT_IRG_SIZE = hti_format_t::ONE_INCH_POS;
|
||||
|
||||
// Formatted size of records: 2.85"
|
||||
static constexpr tape_pos_t FMT_REC_SIZE = 2.85 * hti_format_t::ONE_INCH_POS;
|
||||
// Formatted size of records: 2.67"
|
||||
static constexpr tape_pos_t FMT_REC_SIZE = 2.67 * hti_format_t::ONE_INCH_POS;
|
||||
|
||||
// Starting position on tracks: 74" from beginning of tape
|
||||
static constexpr tape_pos_t TRACK_START = 74 * hti_format_t::ONE_INCH_POS;
|
||||
@ -145,6 +145,7 @@ typedef struct {
|
||||
tape_image_85::tape_image_85(void)
|
||||
: dirty(false)
|
||||
{
|
||||
image.set_bits_per_word(16);
|
||||
}
|
||||
|
||||
void tape_image_85::format_img(void)
|
||||
@ -263,22 +264,24 @@ bool tape_image_85::load_sif_file(file_no_t file_no , sif_file_t& out)
|
||||
if (!image.next_data(track , pos , true , false , it)) {
|
||||
break;
|
||||
}
|
||||
// 0 Sync word
|
||||
// 1 File word
|
||||
// 2 Record word
|
||||
// 3 Length word
|
||||
// 4 Checksum
|
||||
tape_word_t hdr[ 5 ];
|
||||
auto res = hti_format_t::ADV_CONT_DATA;
|
||||
for (unsigned i = 0; i < 5; i++) {
|
||||
unsigned bit_idx = 15;
|
||||
if (!image.sync_with_record(track , it , bit_idx)) {
|
||||
// Couldn't align
|
||||
return false;
|
||||
}
|
||||
|
||||
// 0 File word
|
||||
// 1 Record word
|
||||
// 2 Length word
|
||||
// 3 Checksum
|
||||
tape_word_t hdr[ 4 ];
|
||||
for (unsigned i = 0; i < 4; i++) {
|
||||
auto res = image.next_word(track , it , bit_idx , hdr[ i ]);
|
||||
if (res != hti_format_t::ADV_CONT_DATA) {
|
||||
return false;
|
||||
}
|
||||
hdr[ i ] = it->second;
|
||||
res = image.adv_it(track , true , it);
|
||||
}
|
||||
if (hdr[ 0 ] != SYNC_WORD ||
|
||||
checksum(&hdr[ 1 ] , 3) != hdr[ 4 ]) {
|
||||
if (checksum(&hdr[ 0 ] , 3) != hdr[ 3 ]) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -287,7 +290,7 @@ bool tape_image_85::load_sif_file(file_no_t file_no , sif_file_t& out)
|
||||
bool hdr_has_body;
|
||||
unsigned hdr_body_len;
|
||||
|
||||
if (!dec_rec_header(&hdr[ 1 ] , hdr_file_no , hdr_rec_no , hdr_has_body , hdr_body_len)) {
|
||||
if (!dec_rec_header(&hdr[ 0 ] , hdr_file_no , hdr_rec_no , hdr_has_body , hdr_body_len)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -304,11 +307,10 @@ bool tape_image_85::load_sif_file(file_no_t file_no , sif_file_t& out)
|
||||
tape_word_t body[ MAX_RECORD_SIZE / 2 + 1 ];
|
||||
unsigned word_no = (hdr_body_len + 1) / 2 + 1;
|
||||
for (unsigned i = 0; i < word_no; i++) {
|
||||
auto res = image.next_word(track , it , bit_idx , body[ i ]);
|
||||
if (res != hti_format_t::ADV_CONT_DATA) {
|
||||
return false;
|
||||
}
|
||||
body[ i ] = it->second;
|
||||
res = image.adv_it(track , true , it);
|
||||
}
|
||||
if (checksum(&body[ 0 ] , word_no - 1) != body[ word_no - 1 ]) {
|
||||
return false;
|
||||
|
@ -148,10 +148,10 @@
|
||||
#define BYTES_AVAILABLE_MASK 0xff00 // Mask of "bytes available" field
|
||||
#define BYTES_USED 0x00ff // "bytes used" field = 256
|
||||
#define BYTES_USED_MASK 0x00ff // Mask of "bytes used" field
|
||||
#define FORMAT_SECT_SIZE ((tape_pos_t)(2.85 * hti_format_t::ONE_INCH_POS)) // Size of sectors including padding: 2.85"
|
||||
#define PREAMBLE_WORD 0 // Value of preamble word
|
||||
#define FORMAT_SECT_SIZE ((tape_pos_t)(2.67 * hti_format_t::ONE_INCH_POS)) // Size of sectors including padding: 2.67"
|
||||
#define PREAMBLE_WORD 0x0001 // Value of preamble word
|
||||
#define WORDS_PER_HEADER_N_SECTOR (WORDS_PER_SECTOR + 5)
|
||||
#define MIN_IRG_SIZE ((tape_pos_t)(0.066 * hti_format_t::ONE_INCH_POS)) // Minimum size of IRG gaps: 0.066"
|
||||
#define MIN_IRG_SIZE ((tape_pos_t)(16 * 1024)) // Minimum size of IRG gaps: 0.017"
|
||||
|
||||
// File types
|
||||
#define BKUP_FILETYPE 0
|
||||
@ -324,6 +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);
|
||||
|
||||
io_generic io;
|
||||
io.file = (void *)stream;
|
||||
@ -357,16 +358,19 @@ imgtoolerr_t tape_image_t::load_from_file(imgtool::stream *stream)
|
||||
}
|
||||
if (state == 1) {
|
||||
// Extract record data
|
||||
if (it->second != PREAMBLE_WORD) {
|
||||
|
||||
// The top 8 bits are ignored by TACO when aligning with preamble
|
||||
unsigned bit_idx = 7;
|
||||
if (!inp_image.sync_with_record(track , it , bit_idx)) {
|
||||
// Couldn't align
|
||||
return IMGTOOLERR_CORRUPTIMAGE;
|
||||
}
|
||||
tape_word_t buffer[ WORDS_PER_HEADER_N_SECTOR ];
|
||||
for (unsigned i = 0; i < WORDS_PER_HEADER_N_SECTOR; i++) {
|
||||
auto res = inp_image.adv_it(track , true , it);
|
||||
auto res = inp_image.next_word(track , it , bit_idx , buffer[ i ]);
|
||||
if (res != hti_format_t::ADV_CONT_DATA) {
|
||||
return IMGTOOLERR_CORRUPTIMAGE;
|
||||
}
|
||||
buffer[ i ] = it->second;
|
||||
}
|
||||
if (buffer[ 3 ] != checksum(&buffer[ 0 ], 3) ||
|
||||
buffer[ 4 + WORDS_PER_SECTOR ] != checksum(&buffer[ 4 ], WORDS_PER_SECTOR)) {
|
||||
|
Loading…
Reference in New Issue
Block a user