mirror of
https://github.com/holub/mame
synced 2025-04-22 08:22:15 +03:00
hp9845: Introduced no-data timeouts throughout TACO device
This commit is contained in:
parent
13d8279e49
commit
ae64748f91
@ -131,9 +131,9 @@
|
||||
// validate my solutions by running the original firmware in MAME, though (no real hw at hand).
|
||||
//
|
||||
// TODOs/issues:
|
||||
// * It seems like all commands expecting to read data off the tape have a kind of timeout. More
|
||||
// investigation is needed.
|
||||
// * Emulation of CMD_NOT_INDTA is not entirely ok, SIF utilities fail when using this command.
|
||||
// * Commands 00 & 10 seem to do the same things. My bet is that they differ in some subtle way that
|
||||
// is not stimulated by all the tape software I used for R.E. Maybe it's just the length of the
|
||||
// no-data timeout they have.
|
||||
// * Find more info on TACO chips (does anyone with a working 9845 or access to internal HP docs want to
|
||||
// help me here, please?)
|
||||
//
|
||||
@ -144,7 +144,7 @@
|
||||
// Debugging
|
||||
#define VERBOSE 1
|
||||
#define LOG(x) do { if (VERBOSE) logerror x; } while (0)
|
||||
#define VERBOSE_0 0
|
||||
#define VERBOSE_0 1
|
||||
#define LOG_0(x) do { if (VERBOSE_0) logerror x; } while (0)
|
||||
|
||||
// Macros to clear/set single bits
|
||||
@ -155,7 +155,8 @@
|
||||
// Timers
|
||||
enum {
|
||||
TAPE_TMR_ID,
|
||||
HOLE_TMR_ID
|
||||
HOLE_TMR_ID,
|
||||
TIMEOUT_TMR_ID
|
||||
};
|
||||
|
||||
// Constants
|
||||
@ -182,7 +183,8 @@ enum {
|
||||
#define SHORT_GAP_LENGTH ((tape_pos_t)(0.066 * ONE_INCH_POS)) // Minimum length of short gaps: 0.066" ([1], pg 8-10)
|
||||
#define LONG_GAP_LENGTH ((tape_pos_t)(1.5 * ONE_INCH_POS)) // Minimum length of long gaps: 1.5" ([1], pg 8-10)
|
||||
#define NULL_TAPE_POS ((tape_pos_t)-1) // Special value for invalid/unknown tape position
|
||||
#define NO_DATA_GAP (17 * ONE_BIT_LEN) // Minimum gap size to detect end of data: length of longest word (0xffff)
|
||||
#define PREAMBLE_TIMEOUT ((tape_pos_t)(2.6 * ONE_INCH_POS)) // Min. length of gap making preamble search time out (totally made up)
|
||||
#define DATA_TIMEOUT ((tape_pos_t)(0.066 * ONE_INCH_POS)) // Min. length of gap that will cause data reading to time out (totally made up)
|
||||
#define FILE_MAGIC 0x4f434154 // Magic value at start of image file: "TACO"
|
||||
|
||||
// Parts of command register
|
||||
@ -203,7 +205,7 @@ enum {
|
||||
|
||||
// Commands
|
||||
enum {
|
||||
CMD_INDTA_INGAP, // 00: scan for data first then for gap
|
||||
CMD_INDTA_INGAP, // 00: scan for data first then for gap (see also cmd 10)
|
||||
CMD_UNK_01, // 01: unknown
|
||||
CMD_FINAL_GAP, // 02: write final gap
|
||||
CMD_INIT_WRITE, // 03: write words for tape formatting
|
||||
@ -219,7 +221,7 @@ enum {
|
||||
CMD_UNK_0d, // 0d: unknown
|
||||
CMD_CLEAR, // 0e: clear errors/unlatch status bits
|
||||
CMD_UNK_0f, // 0f: unknown
|
||||
CMD_NOT_INDTA, // 10: scan for end of data
|
||||
CMD_NOT_INDTA, // 10: scan for end of data (at the moment it's the same as cmd 00)
|
||||
CMD_UNK_11, // 11: unknown
|
||||
CMD_UNK_12, // 12: unknown
|
||||
CMD_UNK_13, // 13: unknown
|
||||
@ -422,6 +424,7 @@ void hp_taco_device::device_start()
|
||||
|
||||
m_tape_timer = timer_alloc(TAPE_TMR_ID);
|
||||
m_hole_timer = timer_alloc(HOLE_TMR_ID);
|
||||
m_timeout_timer = timer_alloc(TIMEOUT_TMR_ID);
|
||||
}
|
||||
|
||||
// device_stop
|
||||
@ -467,12 +470,6 @@ void hp_taco_device::device_timer(emu_timer &timer, device_timer_id id, int para
|
||||
m_rw_pos = m_tape_pos;
|
||||
break;
|
||||
|
||||
case CMD_SCAN_RECORDS:
|
||||
// Cmds 18 is terminated at first hole
|
||||
terminate_cmd_now();
|
||||
// No reloading of hole timer
|
||||
return;
|
||||
|
||||
case CMD_MOVE_INGAP:
|
||||
m_hole_timer->adjust(time_to_next_hole());
|
||||
// No IRQ at holes
|
||||
@ -485,10 +482,20 @@ void hp_taco_device::device_timer(emu_timer &timer, device_timer_id id, int para
|
||||
freeze_tach_reg(true);
|
||||
return;
|
||||
|
||||
case CMD_INDTA_INGAP:
|
||||
case CMD_INGAP_MOVE:
|
||||
case CMD_NOT_INDTA:
|
||||
case CMD_SCAN_RECORDS:
|
||||
// Commands are terminated at first hole (and failure is reported)
|
||||
terminate_cmd_now();
|
||||
set_error(true);
|
||||
return;
|
||||
|
||||
case CMD_START_READ:
|
||||
case CMD_END_READ:
|
||||
set_error(true);
|
||||
break;
|
||||
// Commands report failure at first hole
|
||||
set_error(true);
|
||||
break;
|
||||
|
||||
default:
|
||||
// Other cmds: default processing (update tape pos, set IRQ, schedule timer for next hole)
|
||||
@ -500,6 +507,23 @@ void hp_taco_device::device_timer(emu_timer &timer, device_timer_id id, int para
|
||||
m_hole_timer->adjust(time_to_next_hole());
|
||||
break;
|
||||
|
||||
case TIMEOUT_TMR_ID:
|
||||
LOG_0(("T/O tmr @%g cmd %02x st %d\n" , machine().time().as_double() , CMD_CODE(m_cmd_reg) , m_cmd_state));
|
||||
switch (CMD_CODE(m_cmd_reg)) {
|
||||
case CMD_START_READ:
|
||||
if (m_cmd_state == CMD_PH1) {
|
||||
irq_w(true);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
// Most commands are terminated with failure on data T/O
|
||||
terminate_cmd_now();
|
||||
break;
|
||||
}
|
||||
set_error(true);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@ -684,7 +708,7 @@ hp_taco_device::tape_pos_t hp_taco_device::next_hole(void) const
|
||||
}
|
||||
}
|
||||
// No more holes: will hit end of tape
|
||||
return TAPE_LENGTH;
|
||||
return NULL_TAPE_POS;
|
||||
} else {
|
||||
for (int i = (sizeof(tape_holes) / sizeof(tape_holes[ 0 ])) - 1; i >= 0; i--) {
|
||||
if (tape_holes[ i ] < m_tape_pos) {
|
||||
@ -693,7 +717,7 @@ hp_taco_device::tape_pos_t hp_taco_device::next_hole(void) const
|
||||
}
|
||||
}
|
||||
// No more holes: will hit start of tape
|
||||
return 0;
|
||||
return NULL_TAPE_POS;
|
||||
}
|
||||
}
|
||||
|
||||
@ -749,13 +773,15 @@ bool hp_taco_device::start_tape_cmd(UINT16 cmd_reg , UINT16 must_be_1 , UINT16 m
|
||||
BIT_CLR(m_status_reg, STATUS_GAP_BIT);
|
||||
}
|
||||
|
||||
if (!not_moving && !prev_tape_braking && prev_tape_fwd != m_tape_fwd) {
|
||||
if (!not_moving && prev_tape_fwd != m_tape_fwd) {
|
||||
// Tape direction inverted, stop tape before executing command
|
||||
m_tape_fwd = prev_tape_fwd;
|
||||
m_tape_fast = prev_tape_fast;
|
||||
m_cmd_state = CMD_INVERTING;
|
||||
LOG_0(("Direction reversed! fwd = %d fast = %d\n" , m_tape_fwd , m_tape_fast));
|
||||
m_tape_timer->adjust(time_to_stopping_pos());
|
||||
if (!prev_tape_braking) {
|
||||
m_tape_timer->adjust(time_to_stopping_pos());
|
||||
}
|
||||
} else {
|
||||
// No change in direction, immediate execution
|
||||
m_cmd_state = CMD_PH0;
|
||||
@ -763,7 +789,7 @@ bool hp_taco_device::start_tape_cmd(UINT16 cmd_reg , UINT16 must_be_1 , UINT16 m
|
||||
}
|
||||
|
||||
m_hole_timer->reset();
|
||||
|
||||
m_timeout_timer->reset();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@ -1146,7 +1172,13 @@ void hp_taco_device::set_tape_present(bool present)
|
||||
|
||||
attotime hp_taco_device::time_to_next_hole(void) const
|
||||
{
|
||||
return time_to_target(next_hole());
|
||||
tape_pos_t pos = next_hole();
|
||||
|
||||
if (pos == NULL_TAPE_POS) {
|
||||
return attotime::never;
|
||||
} else {
|
||||
return time_to_target(pos);
|
||||
}
|
||||
}
|
||||
|
||||
attotime hp_taco_device::time_to_tach_pulses(void) const
|
||||
@ -1159,6 +1191,13 @@ void hp_taco_device::terminate_cmd_now(void)
|
||||
m_cmd_state = CMD_END;
|
||||
m_tape_timer->adjust(attotime::zero);
|
||||
m_hole_timer->reset();
|
||||
m_timeout_timer->reset();
|
||||
}
|
||||
|
||||
void hp_taco_device::set_data_timeout(bool long_timeout)
|
||||
{
|
||||
attotime timeout = time_to_distance(long_timeout ? PREAMBLE_TIMEOUT : DATA_TIMEOUT);
|
||||
m_timeout_timer->adjust(timeout , 0 , timeout);
|
||||
}
|
||||
|
||||
void hp_taco_device::cmd_fsm(void)
|
||||
@ -1167,6 +1206,7 @@ void hp_taco_device::cmd_fsm(void)
|
||||
// Command ended
|
||||
m_cmd_state = CMD_IDLE;
|
||||
m_hole_timer->reset();
|
||||
m_timeout_timer->reset();
|
||||
irq_w(true);
|
||||
if (AUTO_STOP(m_cmd_reg)) {
|
||||
// Automatic stop after command execution
|
||||
@ -1195,23 +1235,6 @@ void hp_taco_device::cmd_fsm(void)
|
||||
}
|
||||
|
||||
switch (CMD_CODE(m_cmd_reg)) {
|
||||
case CMD_INDTA_INGAP:
|
||||
if (m_cmd_state == CMD_PH0) {
|
||||
// PH0
|
||||
if (next_data(m_rd_it , m_tape_pos , true)) {
|
||||
cmd_duration = time_to_target(farthest_end(m_rd_it));
|
||||
}
|
||||
m_cmd_state = CMD_PH1;
|
||||
} else {
|
||||
// PH1
|
||||
tape_pos_t target = m_tape_pos;
|
||||
if (next_n_gap(target, 1, min_gap_size())) {
|
||||
cmd_duration = time_to_target(target);
|
||||
}
|
||||
m_cmd_state = CMD_END;
|
||||
}
|
||||
break;
|
||||
|
||||
case CMD_FINAL_GAP:
|
||||
if (m_cmd_state == CMD_PH0) {
|
||||
// PH0
|
||||
@ -1232,6 +1255,8 @@ void hp_taco_device::cmd_fsm(void)
|
||||
// Search for preamble first
|
||||
m_rd_it_valid = next_data(m_rd_it , m_tape_pos , false);
|
||||
cmd_duration = time_to_rd_next_word(m_rw_pos);
|
||||
// Set T/O for preamble search
|
||||
set_data_timeout(true);
|
||||
m_cmd_state = CMD_PH1;
|
||||
break;
|
||||
} else if (m_cmd_state == CMD_PH1) {
|
||||
@ -1241,12 +1266,15 @@ void hp_taco_device::cmd_fsm(void)
|
||||
m_cmd_state = CMD_PH2;
|
||||
// m_rw_pos already at correct position
|
||||
cmd_duration = fetch_next_wr_word();
|
||||
m_timeout_timer->reset();
|
||||
irq_w(true);
|
||||
} else {
|
||||
adv_res_t res = adv_it(m_rd_it);
|
||||
if (res != ADV_NO_MORE_DATA) {
|
||||
cmd_duration = time_to_rd_next_word(m_rw_pos);
|
||||
}
|
||||
// Set T/O for arrival of data words
|
||||
set_data_timeout(false);
|
||||
}
|
||||
break;
|
||||
}
|
||||
@ -1310,20 +1338,25 @@ void hp_taco_device::cmd_fsm(void)
|
||||
}
|
||||
break;
|
||||
|
||||
case CMD_INDTA_INGAP:
|
||||
case CMD_NOT_INDTA:
|
||||
if (m_cmd_state == CMD_PH0) {
|
||||
// PH0
|
||||
if (next_data(m_rd_it , m_tape_pos , true)) {
|
||||
cmd_duration = time_to_target(farthest_end(m_rd_it));
|
||||
}
|
||||
// Set T/O for data
|
||||
set_data_timeout(true);
|
||||
m_cmd_state = CMD_PH1;
|
||||
} else {
|
||||
// PH1
|
||||
tape_pos_t target = m_tape_pos;
|
||||
if (next_n_gap(target, 1, NO_DATA_GAP)) {
|
||||
if (next_n_gap(target, 1, min_gap_size())) {
|
||||
LOG_0(("End of data @%d\n" , target));
|
||||
cmd_duration = time_to_target(target);
|
||||
}
|
||||
// Got data, stop T/O
|
||||
m_timeout_timer->reset();
|
||||
m_cmd_state = CMD_END;
|
||||
}
|
||||
break;
|
||||
@ -1344,16 +1377,20 @@ void hp_taco_device::cmd_fsm(void)
|
||||
break;
|
||||
|
||||
case CMD_SCAN_RECORDS:
|
||||
// It's probably more correct to implement this to alternate between next data and next gap by using different
|
||||
// FSM states. It times out sooner if the record being searched for doesn't exist on tape. With the current
|
||||
// implementation it has to wait until a hole is reached at either end of the tape.
|
||||
if (m_cmd_state == CMD_PH0) {
|
||||
// PH0
|
||||
if (next_data(m_rd_it , m_tape_pos , true)) {
|
||||
cmd_duration = time_to_target(farthest_end(m_rd_it));
|
||||
}
|
||||
// Set T/O for data
|
||||
set_data_timeout(true);
|
||||
m_cmd_state = CMD_PH1;
|
||||
} else {
|
||||
// PH1
|
||||
tape_pos_t target = m_tape_pos;
|
||||
// b8 seems to select size of gaps
|
||||
// Tach. register is incremented at each gap. Command ends when this register goes positive (b15 = 0).
|
||||
unsigned n_gaps;
|
||||
if (BIT(m_tach_reg , 15)) {
|
||||
@ -1367,6 +1404,7 @@ void hp_taco_device::cmd_fsm(void)
|
||||
LOG_0(("%u gaps @%d\n" , n_gaps, target));
|
||||
cmd_duration = time_to_target(target);
|
||||
}
|
||||
m_timeout_timer->reset();
|
||||
m_cmd_state = CMD_END;
|
||||
}
|
||||
break;
|
||||
@ -1382,6 +1420,8 @@ void hp_taco_device::cmd_fsm(void)
|
||||
if (next_data(m_rd_it , m_tape_pos , true)) {
|
||||
cmd_duration = time_to_target(farthest_end(m_rd_it));
|
||||
}
|
||||
// Set T/O for data
|
||||
set_data_timeout(true);
|
||||
m_cmd_state = CMD_END;
|
||||
}
|
||||
break;
|
||||
@ -1417,23 +1457,18 @@ void hp_taco_device::cmd_fsm(void)
|
||||
if (!m_rd_it_valid) {
|
||||
// Search for preamble first
|
||||
m_rd_it_valid = next_data(m_rd_it , m_tape_pos , false);
|
||||
// Set T/O for preamble search
|
||||
set_data_timeout(true);
|
||||
|
||||
m_cmd_state = CMD_PH1;
|
||||
} else {
|
||||
// Resume reading from last position, skip preamble search
|
||||
m_cmd_state = CMD_PH2;
|
||||
}
|
||||
|
||||
cmd_duration = time_to_rd_next_word(m_rw_pos);
|
||||
} else {
|
||||
// Just to be sure..
|
||||
m_tape_pos = m_rw_pos;
|
||||
|
||||
if (m_cmd_state == CMD_PH3) {
|
||||
// PH3: delayed setting of error condition
|
||||
set_error(true);
|
||||
m_cmd_state = CMD_PH2;
|
||||
}
|
||||
|
||||
if (m_cmd_state == CMD_PH1) {
|
||||
// PH1
|
||||
// Any word with at least a 0 will do as preamble.
|
||||
@ -1445,6 +1480,9 @@ void hp_taco_device::cmd_fsm(void)
|
||||
}
|
||||
} else {
|
||||
// PH2
|
||||
if (m_irq) {
|
||||
LOG(("Data reg overflow!\n"));
|
||||
}
|
||||
irq_w(true);
|
||||
m_data_reg = m_rd_it->second;
|
||||
if (m_clear_checksum_reg) {
|
||||
@ -1454,24 +1492,15 @@ void hp_taco_device::cmd_fsm(void)
|
||||
m_checksum_reg += m_data_reg;
|
||||
LOG_0(("RD %04x\n" , m_data_reg));
|
||||
}
|
||||
// Set T/O for arrival of data words
|
||||
set_data_timeout(false);
|
||||
adv_res_t res = adv_it(m_rd_it);
|
||||
LOG_0(("adv_it %d\n" , res));
|
||||
if (res == ADV_NO_MORE_DATA) {
|
||||
m_rd_it_valid = false;
|
||||
} else {
|
||||
cmd_duration = time_to_rd_next_word(m_rw_pos);
|
||||
if (res == ADV_DISCONT_DATA) {
|
||||
// Wild guess: TACO sets error flag when it stumbles on a gap between words
|
||||
if (m_cmd_state == CMD_PH2 && abs(m_tape_pos - m_rw_pos) > ((tape_pos_t)(0.25 * ONE_INCH_POS))) {
|
||||
m_cmd_state = CMD_PH3;
|
||||
} else {
|
||||
// Hit a gap, restart preamble search
|
||||
// TODO: is this ok?
|
||||
m_cmd_state = CMD_PH1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
cmd_duration = time_to_rd_next_word(m_rw_pos);
|
||||
break;
|
||||
|
||||
case CMD_DELTA_MOVE_IRG:
|
||||
@ -1485,6 +1514,7 @@ void hp_taco_device::cmd_fsm(void)
|
||||
if (m_cmd_state == CMD_PH0) {
|
||||
// PH0
|
||||
cmd_duration = time_to_rd_next_word(m_rw_pos);
|
||||
set_data_timeout(false);
|
||||
m_cmd_state = CMD_PH1;
|
||||
} else {
|
||||
// PH1
|
||||
@ -1545,7 +1575,8 @@ void hp_taco_device::start_cmd_exec(UINT16 new_cmd_reg)
|
||||
m_cmd_reg = new_cmd_reg;
|
||||
freeze_tach_reg(false);
|
||||
m_hole_timer->reset();
|
||||
if (m_cmd_state == CMD_INVERTING || m_cmd_state == CMD_STOPPING) {
|
||||
m_timeout_timer->reset();
|
||||
if (is_braking()) {
|
||||
// Already braking
|
||||
// m_tape_timer already set
|
||||
} else if (m_start_time.is_never()) {
|
||||
@ -1585,8 +1616,8 @@ void hp_taco_device::start_cmd_exec(UINT16 new_cmd_reg)
|
||||
return;
|
||||
|
||||
case CMD_NOT_INDTA:
|
||||
// Errors: CART OUT,FAST SPEED
|
||||
started = start_tape_cmd(new_cmd_reg , 0 , SPEED_FAST_MASK);
|
||||
// Errors: CART OUT
|
||||
started = start_tape_cmd(new_cmd_reg , 0 , 0);
|
||||
break;
|
||||
|
||||
case CMD_WRITE_IRG:
|
||||
@ -1629,8 +1660,7 @@ void hp_taco_device::start_cmd_exec(UINT16 new_cmd_reg)
|
||||
|
||||
case CMD_END_READ:
|
||||
// This command only makes sense after CMD_START_READ
|
||||
if (CMD_CODE(m_cmd_reg) == CMD_START_READ &&
|
||||
(m_cmd_state == CMD_PH2 || m_cmd_state == CMD_PH3)) {
|
||||
if (CMD_CODE(m_cmd_reg) == CMD_START_READ) {
|
||||
started = start_tape_cmd(new_cmd_reg , 0 , SPEED_FAST_MASK);
|
||||
LOG_0(("END_READ %d\n" , m_rd_it_valid));
|
||||
}
|
||||
@ -1647,6 +1677,7 @@ void hp_taco_device::start_cmd_exec(UINT16 new_cmd_reg)
|
||||
m_cmd_state = CMD_IDLE;
|
||||
m_tape_timer->reset();
|
||||
m_hole_timer->reset();
|
||||
m_timeout_timer->reset();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -103,7 +103,6 @@ private:
|
||||
CMD_PH0,
|
||||
CMD_PH1,
|
||||
CMD_PH2,
|
||||
CMD_PH3,
|
||||
CMD_END,
|
||||
CMD_STOPPING
|
||||
} cmd_state_t;
|
||||
@ -118,6 +117,7 @@ private:
|
||||
// Timers
|
||||
emu_timer *m_tape_timer;
|
||||
emu_timer *m_hole_timer;
|
||||
emu_timer *m_timeout_timer;
|
||||
|
||||
// Content of tape tracks
|
||||
tape_track_t m_tracks[ 2 ];
|
||||
@ -180,6 +180,7 @@ private:
|
||||
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 start_cmd_exec(UINT16 new_cmd_reg);
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user