mirror of
https://github.com/holub/mame
synced 2025-04-16 13:34:55 +03:00
hp9845: initial version of HP TACO driver (only basic tape movement is working)
This commit is contained in:
parent
11f82be540
commit
408c6351cf
@ -771,6 +771,18 @@ if (MACHINES["HD64610"]~=null) then
|
||||
}
|
||||
end
|
||||
|
||||
---------------------------------------------------
|
||||
--
|
||||
--@src/devices/machine/hp_taco.h,MACHINES["HP_TACO"] = true
|
||||
---------------------------------------------------
|
||||
|
||||
if (MACHINES["HP_TACO"]~=null) then
|
||||
files {
|
||||
MAME_DIR .. "src/devices/machine/hp_taco.cpp",
|
||||
MAME_DIR .. "src/devices/machine/hp_taco.h",
|
||||
}
|
||||
end
|
||||
|
||||
---------------------------------------------------
|
||||
--
|
||||
--@src/devices/machine/i2cmem.h,MACHINES["I2CMEM"] = true
|
||||
|
@ -398,6 +398,7 @@ MACHINES["ER2055"] = true
|
||||
MACHINES["F3853"] = true
|
||||
MACHINES["HD63450"] = true
|
||||
MACHINES["HD64610"] = true
|
||||
MACHINES["HP_TACO"] = true
|
||||
MACHINES["I2CMEM"] = true
|
||||
MACHINES["I80130"] = true
|
||||
MACHINES["I8089"] = true
|
||||
|
516
src/devices/machine/hp_taco.cpp
Normal file
516
src/devices/machine/hp_taco.cpp
Normal file
@ -0,0 +1,516 @@
|
||||
// license:BSD-3-Clause
|
||||
// copyright-holders:F. Ulivi
|
||||
/*********************************************************************
|
||||
|
||||
hp_taco.cpp
|
||||
|
||||
HP TApe COntroller (5006-3012)
|
||||
|
||||
*********************************************************************/
|
||||
|
||||
// Documentation I used:
|
||||
// [1] HP, manual 64940-90905, may 80 rev. - Model 64940A tape control & drive service manual
|
||||
// [2] US patent 4,075,679 describing HP9825 system (this system had a discrete implementation of tape controller)
|
||||
|
||||
// Format of TACO command/status register (R5)
|
||||
// Bit R/W Content
|
||||
// ===============
|
||||
// 15 RW Tape direction (1 = forward)
|
||||
// 14..10 RW Command
|
||||
// 9 RW ? Drive ON according to [1], doesn't match usage of firmware
|
||||
// 8 RW ? Size of gaps according to [1]
|
||||
// 7 RW Speed of tape (1 = 90 ips, 0 = 22 ips)
|
||||
// 6 RW Option bit for various commands
|
||||
// 5 R Current track (1 = B)
|
||||
// 4 R Gap detected (1)
|
||||
// 3 R Write protection (1)
|
||||
// 2 R Servo failure (1)
|
||||
// 1 R Cartridge out (1)
|
||||
// 0 R Hole detected (1)
|
||||
|
||||
// TODO: R6 è modificato durante il conteggio impulsi? Viene azzerato alla lettura?
|
||||
|
||||
#include "emu.h"
|
||||
#include "hp_taco.h"
|
||||
|
||||
// Debugging
|
||||
#define VERBOSE 1
|
||||
#define LOG(x) do { if (VERBOSE) logerror x; } while (0)
|
||||
|
||||
// Macros to clear/set single bits
|
||||
#define BIT_MASK(n) (1U << (n))
|
||||
#define BIT_CLR(w , n) ((w) &= ~BIT_MASK(n))
|
||||
#define BIT_SET(w , n) ((w) |= BIT_MASK(n))
|
||||
|
||||
// Timers
|
||||
enum {
|
||||
TAPE_TMR_ID
|
||||
};
|
||||
|
||||
// Constants
|
||||
#define CMD_REG_MASK 0xffc0 // Command register mask
|
||||
#define STATUS_REG_MASK 0x003f // Status register mask
|
||||
#define STATUS_ERR_MASK 0x0002 // Mask of errors in status reg.
|
||||
#define TACH_TICKS_PER_INCH 968 // Tachometer pulses per inch of tape movement
|
||||
#define TACH_FREQ_SLOW 21276 // Tachometer pulse frequency for slow speed (21.98 ips)
|
||||
#define TACH_FREQ_FAST 87196 // Tachometer pulse frequency for fast speed (90.08 ips)
|
||||
#define TAPE_LENGTH ((140 * 12 + 72 * 2) * TACH_TICKS_PER_INCH) // Tape length (in tachometer pulses): 140 ft of usable tape + 72" of punched tape at either end
|
||||
#define TAPE_INIT_POS (80 * TACH_TICKS_PER_INCH) // Initial tape position: 80" from beginning (just past the punched part)
|
||||
#define QUICK_CMD_USEC 10 // usec for "quick" command execution
|
||||
|
||||
// Parts of command register
|
||||
#define CMD_CODE(reg) \
|
||||
(((reg) >> 10) & 0x1f)
|
||||
#define DIR_FWD(reg) \
|
||||
(BIT(reg , 15))
|
||||
#define SPEED_FAST(reg) \
|
||||
(BIT(reg , 7))
|
||||
#define CMD_OPT(reg) \
|
||||
(BIT(reg , 6))
|
||||
|
||||
// Commands
|
||||
enum {
|
||||
CMD_ALIGN_0, // 00: header alignment (?)
|
||||
CMD_UNK_01, // 01: unknown
|
||||
CMD_FINAL_GAP, // 02: write final gap
|
||||
CMD_INIT_WRITE, // 03: write words for tape formatting
|
||||
CMD_STOP, // 04: stop
|
||||
CMD_UNK_05, // 05: unknown
|
||||
CMD_SET_TRACK, // 06: set A/B track
|
||||
CMD_UNK_07, // 07: unknown
|
||||
CMD_UNK_08, // 08: unknown
|
||||
CMD_UNK_09, // 09: unknown
|
||||
CMD_MOVE, // 0a: move tape
|
||||
CMD_UNK_0b, // 0b: unknown
|
||||
CMD_UNK_0c, // 0c: unknown*
|
||||
CMD_UNK_0d, // 0d: unknown
|
||||
CMD_CLEAR, // 0e: clear errors/unlatch status bits
|
||||
CMD_UNK_0f, // 0f: unknown
|
||||
CMD_ALIGN_PREAMBLE, // 10: align to end of preamble (?)
|
||||
CMD_UNK_11, // 11: unknown
|
||||
CMD_UNK_12, // 12: unknown
|
||||
CMD_UNK_13, // 13: unknown
|
||||
CMD_UNK_14, // 14: unknown
|
||||
CMD_UNK_15, // 15: unknown
|
||||
CMD_WRITE_IRG, // 16: write inter-record gap
|
||||
CMD_UNK_17, // 17: unknown
|
||||
CMD_SCAN_RECORDS, // 18: scan records (count IRGs)
|
||||
CMD_RECORD_WRITE, // 19: write record words
|
||||
CMD_UNK_MOVE, // 1a: some kind of tape movement
|
||||
CMD_UNK_1b, // 1b: unknown
|
||||
CMD_DELTA_MOVE_REC, // 1c: move tape a given distance (optionally stop at 1st record) (?)
|
||||
CMD_START_READ, // 1d: start record reading
|
||||
CMD_DELTA_MOVE_IRG, // 1e: move tape a given distance (optionally stop at 1st IRG)
|
||||
CMD_END_READ // 1f: stop reading
|
||||
};
|
||||
|
||||
// Bits of status register
|
||||
#define STATUS_HOLE_BIT 0 // Hole detected
|
||||
#define STATUS_CART_OUT_BIT 1 // Cartridge out
|
||||
#define STATUS_SFAIL_BIT 2 // Servo failure
|
||||
#define STATUS_WPR_BIT 3 // Write protection
|
||||
#define STATUS_GAP_BIT 4 // Gap detected
|
||||
#define STATUS_TRACKB_BIT 5 // Track B selected
|
||||
|
||||
// *** Position of tape holes ***
|
||||
// At beginning of tape:
|
||||
// *START*
|
||||
// |<-----24"----->|<---12"--->|<---12"--->|<-----24"----->|
|
||||
// O O O O O O O
|
||||
// |<->| |<->| |<->|
|
||||
// 0.218" 0.218" 0.218"
|
||||
// At end of tape:
|
||||
// *END*
|
||||
// |<-----24"----->|<---12"--->|<---12"--->|<-----24"----->|
|
||||
// O O O O
|
||||
//
|
||||
static const hp_taco_device::tape_pos_t tape_holes[] = {
|
||||
(hp_taco_device::tape_pos_t)(23.891 * TACH_TICKS_PER_INCH), // 24 - 0.218 / 2
|
||||
(hp_taco_device::tape_pos_t)(24.109 * TACH_TICKS_PER_INCH), // 24 + 0.218 / 2
|
||||
(hp_taco_device::tape_pos_t)(35.891 * TACH_TICKS_PER_INCH), // 36 - 0.218 / 2
|
||||
(hp_taco_device::tape_pos_t)(36.109 * TACH_TICKS_PER_INCH), // 36 + 0.218 / 2
|
||||
(hp_taco_device::tape_pos_t)(47.891 * TACH_TICKS_PER_INCH), // 48 - 0.218 / 2
|
||||
(hp_taco_device::tape_pos_t)(48.109 * TACH_TICKS_PER_INCH), // 48 + 0.218 / 2
|
||||
72 * TACH_TICKS_PER_INCH, // 72
|
||||
1752 * TACH_TICKS_PER_INCH, // 1752
|
||||
1776 * TACH_TICKS_PER_INCH, // 1776
|
||||
1788 * TACH_TICKS_PER_INCH, // 1788
|
||||
1800 * TACH_TICKS_PER_INCH // 1800
|
||||
};
|
||||
|
||||
// Device type definition
|
||||
const device_type HP_TACO = &device_creator<hp_taco_device>;
|
||||
|
||||
// Constructors
|
||||
hp_taco_device::hp_taco_device(const machine_config &mconfig, device_type type, const char *name, const char *tag, device_t *owner, UINT32 clock, const char *shortname)
|
||||
: device_t(mconfig, type, name, tag, owner, clock, shortname, __FILE__),
|
||||
m_irq_handler(*this),
|
||||
m_flg_handler(*this),
|
||||
m_sts_handler(*this),
|
||||
m_data_reg(0),
|
||||
m_cmd_reg(0),
|
||||
m_status_reg(0),
|
||||
m_tach_reg(0),
|
||||
m_checksum_reg(0),
|
||||
m_timing_reg(0),
|
||||
m_tape_pos(TAPE_INIT_POS)
|
||||
{
|
||||
}
|
||||
|
||||
hp_taco_device::hp_taco_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
|
||||
: device_t(mconfig, HP_TACO, "HP TACO", tag, owner, clock, "TACO", __FILE__),
|
||||
m_irq_handler(*this),
|
||||
m_flg_handler(*this),
|
||||
m_sts_handler(*this),
|
||||
m_data_reg(0),
|
||||
m_cmd_reg(0),
|
||||
m_status_reg(0),
|
||||
m_tach_reg(0),
|
||||
m_checksum_reg(0),
|
||||
m_timing_reg(0),
|
||||
m_tape_pos(TAPE_INIT_POS)
|
||||
{
|
||||
}
|
||||
|
||||
WRITE16_MEMBER(hp_taco_device::reg_w)
|
||||
{
|
||||
LOG(("wr R%u = %04x\n", 4 + offset , data));
|
||||
|
||||
// Any I/O activity clears IRQ
|
||||
irq_w(false);
|
||||
|
||||
switch (offset) {
|
||||
case 0:
|
||||
// Data register
|
||||
m_data_reg = data;
|
||||
break;
|
||||
|
||||
case 1:
|
||||
// Command register
|
||||
start_cmd_exec(data & CMD_REG_MASK);
|
||||
break;
|
||||
|
||||
case 2:
|
||||
// Tachometer register
|
||||
m_tach_reg = data;
|
||||
break;
|
||||
|
||||
case 3:
|
||||
// Timing register
|
||||
m_timing_reg = data;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
READ16_MEMBER(hp_taco_device::reg_r)
|
||||
{
|
||||
UINT16 res = 0;
|
||||
|
||||
// Any I/O activity clears IRQ
|
||||
irq_w(false);
|
||||
|
||||
switch (offset) {
|
||||
case 0:
|
||||
// Data register
|
||||
res = m_data_reg;
|
||||
break;
|
||||
|
||||
case 1:
|
||||
// Command & status register
|
||||
res = (m_cmd_reg & CMD_REG_MASK) | (m_status_reg & STATUS_REG_MASK);
|
||||
break;
|
||||
|
||||
case 2:
|
||||
// Tachometer register
|
||||
res = m_tach_reg;
|
||||
break;
|
||||
|
||||
case 3:
|
||||
// Checksum register
|
||||
res = m_checksum_reg;
|
||||
m_checksum_reg = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
LOG(("rd R%u = %04x\n", 4 + offset , res));
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
READ_LINE_MEMBER(hp_taco_device::flg_r)
|
||||
{
|
||||
return m_flg;
|
||||
}
|
||||
|
||||
READ_LINE_MEMBER(hp_taco_device::sts_r)
|
||||
{
|
||||
return m_sts;
|
||||
}
|
||||
|
||||
// device_start
|
||||
void hp_taco_device::device_start()
|
||||
{
|
||||
m_irq_handler.resolve_safe();
|
||||
m_flg_handler.resolve_safe();
|
||||
m_sts_handler.resolve_safe();
|
||||
|
||||
save_item(NAME(m_data_reg));
|
||||
save_item(NAME(m_cmd_reg));
|
||||
save_item(NAME(m_status_reg));
|
||||
save_item(NAME(m_tach_reg));
|
||||
save_item(NAME(m_checksum_reg));
|
||||
save_item(NAME(m_timing_reg));
|
||||
save_item(NAME(m_irq));
|
||||
save_item(NAME(m_flg));
|
||||
save_item(NAME(m_sts));
|
||||
save_item(NAME(m_tape_pos));
|
||||
save_item(NAME(m_start_time));
|
||||
|
||||
m_tape_timer = timer_alloc(TAPE_TMR_ID);
|
||||
}
|
||||
|
||||
// device_reset
|
||||
void hp_taco_device::device_reset()
|
||||
{
|
||||
m_data_reg = 0;
|
||||
m_cmd_reg = 0;
|
||||
m_status_reg = 0;
|
||||
m_tach_reg = 0;
|
||||
m_checksum_reg = 0;
|
||||
m_timing_reg = 0;
|
||||
m_start_time = attotime::never;
|
||||
|
||||
m_irq = false;
|
||||
m_flg = true;
|
||||
|
||||
m_irq_handler(false);
|
||||
m_flg_handler(true);
|
||||
set_error(false);
|
||||
}
|
||||
|
||||
void hp_taco_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr)
|
||||
{
|
||||
switch (id) {
|
||||
case TAPE_TMR_ID:
|
||||
LOG(("Tape tmr @%g\n" , machine().time().as_double()));
|
||||
switch (CMD_CODE(m_cmd_reg)) {
|
||||
case CMD_MOVE:
|
||||
// Generate an interrupt each time a hole is crossed (tape doesn't stop)
|
||||
update_tape_pos();
|
||||
m_tape_timer->adjust(time_to_target(next_hole(DIR_FWD(m_cmd_reg)) , SPEED_FAST(m_cmd_reg)));
|
||||
BIT_SET(m_status_reg, STATUS_HOLE_BIT);
|
||||
break;
|
||||
|
||||
case CMD_DELTA_MOVE_REC:
|
||||
case CMD_DELTA_MOVE_IRG:
|
||||
// Interrupt & stop at end of movement
|
||||
stop_tape();
|
||||
break;
|
||||
|
||||
default:
|
||||
// Other commands: just raise irq
|
||||
break;
|
||||
}
|
||||
irq_w(true);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void hp_taco_device::irq_w(bool state)
|
||||
{
|
||||
if (state != m_irq) {
|
||||
m_irq = state;
|
||||
m_irq_handler(state);
|
||||
LOG(("IRQ = %d\n" , state));
|
||||
}
|
||||
}
|
||||
|
||||
void hp_taco_device::set_error(bool state)
|
||||
{
|
||||
m_sts = !state;
|
||||
m_sts_handler(m_sts);
|
||||
LOG(("error = %d\n" , state));
|
||||
}
|
||||
|
||||
bool hp_taco_device::check_for_errors(void)
|
||||
{
|
||||
// Is it an error when "status" flag is already reporting an error? Dunno...
|
||||
if ((m_status_reg & STATUS_ERR_MASK) != 0) {
|
||||
set_error(true);
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
unsigned hp_taco_device::speed_to_tick_freq(bool fast)
|
||||
{
|
||||
return fast ? TACH_FREQ_FAST : TACH_FREQ_SLOW;
|
||||
}
|
||||
|
||||
void hp_taco_device::update_tape_pos(void)
|
||||
{
|
||||
attotime delta_time(machine().time() - m_start_time);
|
||||
m_start_time = machine().time();
|
||||
LOG(("delta_time = %g\n" , delta_time.as_double()));
|
||||
// How many tachometer ticks has the tape moved?
|
||||
unsigned delta_tach = (unsigned)(delta_time.as_ticks(speed_to_tick_freq(SPEED_FAST(m_cmd_reg))));
|
||||
LOG(("delta_tach = %u\n" , delta_tach));
|
||||
|
||||
if (DIR_FWD(m_cmd_reg)) {
|
||||
// Forward
|
||||
m_tape_pos += delta_tach;
|
||||
|
||||
// In real life tape would unspool..
|
||||
if (m_tape_pos > TAPE_LENGTH) {
|
||||
m_tape_pos = TAPE_LENGTH;
|
||||
LOG(("Tape unspooled at the end!\n"));
|
||||
}
|
||||
} else {
|
||||
// Reverse
|
||||
if (delta_tach >= m_tape_pos) {
|
||||
m_tape_pos = 0;
|
||||
LOG(("Tape unspooled at the start!\n"));
|
||||
} else {
|
||||
m_tape_pos -= delta_tach;
|
||||
}
|
||||
}
|
||||
LOG(("Tape pos = %u\n" , m_tape_pos));
|
||||
}
|
||||
|
||||
// Is there any hole in a given section of tape?
|
||||
bool hp_taco_device::any_hole(tape_pos_t tape_pos_a , tape_pos_t tape_pos_b)
|
||||
{
|
||||
if (tape_pos_a > tape_pos_b) {
|
||||
// Ensure A always comes before B
|
||||
tape_pos_t tmp;
|
||||
tmp = tape_pos_a;
|
||||
tape_pos_a = tape_pos_b;
|
||||
tape_pos_b = tmp;
|
||||
}
|
||||
|
||||
for (tape_pos_t hole : tape_holes) {
|
||||
if (tape_pos_a < hole && tape_pos_b >= hole) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Position of next hole tape will reach in a given direction
|
||||
hp_taco_device::tape_pos_t hp_taco_device::next_hole(bool fwd) const
|
||||
{
|
||||
if (fwd) {
|
||||
for (tape_pos_t hole : tape_holes) {
|
||||
if (hole > m_tape_pos) {
|
||||
LOG(("next hole fwd @%u = %u\n" , m_tape_pos , hole));
|
||||
return hole;
|
||||
}
|
||||
}
|
||||
// No more holes: will hit end of tape
|
||||
return TAPE_LENGTH;
|
||||
} else {
|
||||
for (int i = (sizeof(tape_holes) / sizeof(tape_holes[ 0 ])) - 1; i >= 0; i--) {
|
||||
if (tape_holes[ i ] < m_tape_pos) {
|
||||
LOG(("next hole rev @%u = %u\n" , m_tape_pos , tape_holes[ i ]));
|
||||
return tape_holes[ i ];
|
||||
}
|
||||
}
|
||||
// No more holes: will hit start of tape
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
attotime hp_taco_device::time_to_distance(tape_pos_t distance, bool fast)
|
||||
{
|
||||
// +1 for rounding
|
||||
return attotime::from_ticks(distance + 1 , speed_to_tick_freq(fast));
|
||||
}
|
||||
|
||||
attotime hp_taco_device::time_to_target(tape_pos_t target, bool fast) const
|
||||
{
|
||||
return time_to_distance(abs(target - m_tape_pos), fast);
|
||||
}
|
||||
|
||||
void hp_taco_device::start_tape(void)
|
||||
{
|
||||
m_start_time = machine().time();
|
||||
BIT_CLR(m_status_reg, STATUS_HOLE_BIT);
|
||||
}
|
||||
|
||||
void hp_taco_device::stop_tape(void)
|
||||
{
|
||||
if (!m_start_time.is_never()) {
|
||||
tape_pos_t tape_start_pos = m_tape_pos;
|
||||
update_tape_pos();
|
||||
if (any_hole(tape_start_pos , m_tape_pos)) {
|
||||
// Crossed one or more holes
|
||||
BIT_SET(m_status_reg , STATUS_HOLE_BIT);
|
||||
}
|
||||
m_start_time = attotime::never;
|
||||
}
|
||||
m_tape_timer->reset();
|
||||
}
|
||||
|
||||
void hp_taco_device::start_cmd_exec(UINT16 new_cmd_reg)
|
||||
{
|
||||
LOG(("Cmd = %02x\n" , CMD_CODE(new_cmd_reg)));
|
||||
|
||||
attotime cmd_duration = attotime::never;
|
||||
|
||||
// Should irq be raised anyway when already in error condition? Here we do nothing.
|
||||
|
||||
switch (CMD_CODE(new_cmd_reg)) {
|
||||
case CMD_CLEAR:
|
||||
set_error(false);
|
||||
BIT_CLR(m_status_reg, STATUS_HOLE_BIT);
|
||||
// This is a special command: it doesn't raise IRQ at completion and it
|
||||
// doesn't replace the current command, if any.
|
||||
return;
|
||||
|
||||
case CMD_STOP:
|
||||
stop_tape();
|
||||
cmd_duration = attotime::from_usec(QUICK_CMD_USEC);
|
||||
break;
|
||||
|
||||
case CMD_SET_TRACK:
|
||||
if (!check_for_errors()) {
|
||||
if (CMD_OPT(new_cmd_reg)) {
|
||||
BIT_SET(m_status_reg, STATUS_TRACKB_BIT);
|
||||
} else {
|
||||
BIT_CLR(m_status_reg, STATUS_TRACKB_BIT);
|
||||
}
|
||||
cmd_duration = attotime::from_usec(QUICK_CMD_USEC);
|
||||
}
|
||||
break;
|
||||
|
||||
case CMD_MOVE:
|
||||
stop_tape();
|
||||
if (!check_for_errors()) {
|
||||
start_tape();
|
||||
cmd_duration = time_to_target(next_hole(DIR_FWD(new_cmd_reg)) , SPEED_FAST(new_cmd_reg));
|
||||
}
|
||||
break;
|
||||
|
||||
case CMD_DELTA_MOVE_REC:
|
||||
case CMD_DELTA_MOVE_IRG:
|
||||
// TODO: record/irg detection
|
||||
stop_tape();
|
||||
if (!check_for_errors()) {
|
||||
start_tape();
|
||||
cmd_duration = time_to_distance(0x10000U - m_tach_reg , SPEED_FAST(new_cmd_reg));
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
LOG(("Unrecognized command\n"));
|
||||
return;
|
||||
}
|
||||
|
||||
m_tape_timer->adjust(cmd_duration);
|
||||
m_cmd_reg = new_cmd_reg;
|
||||
}
|
93
src/devices/machine/hp_taco.h
Normal file
93
src/devices/machine/hp_taco.h
Normal file
@ -0,0 +1,93 @@
|
||||
// license:BSD-3-Clause
|
||||
// copyright-holders:F. Ulivi
|
||||
/*********************************************************************
|
||||
|
||||
hp_taco.h
|
||||
|
||||
HP TApe COntroller (5006-3012)
|
||||
|
||||
*********************************************************************/
|
||||
|
||||
#ifndef __HP_TACO_H__
|
||||
#define __HP_TACO_H__
|
||||
|
||||
#define MCFG_TACO_IRQ_HANDLER(_devcb) \
|
||||
devcb = &hp_taco_device::set_irq_handler(*device , DEVCB_##_devcb);
|
||||
|
||||
#define MCFG_TACO_FLG_HANDLER(_devcb) \
|
||||
devcb = &hp_taco_device::set_flg_handler(*device , DEVCB_##_devcb);
|
||||
|
||||
#define MCFG_TACO_STS_HANDLER(_devcb) \
|
||||
devcb = &hp_taco_device::set_sts_handler(*device , DEVCB_##_devcb);
|
||||
|
||||
class hp_taco_device : public device_t
|
||||
{
|
||||
public:
|
||||
// construction/destruction
|
||||
hp_taco_device(const machine_config &mconfig, device_type type, const char *name, const char *tag, device_t *owner, UINT32 clock, const char *shortname);
|
||||
hp_taco_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
|
||||
|
||||
// static configuration helpers
|
||||
template<class _Object> static devcb_base &set_irq_handler(device_t &device, _Object object) { return downcast<hp_taco_device &>(device).m_irq_handler.set_callback(object); }
|
||||
template<class _Object> static devcb_base &set_flg_handler(device_t &device, _Object object) { return downcast<hp_taco_device &>(device).m_flg_handler.set_callback(object); }
|
||||
template<class _Object> static devcb_base &set_sts_handler(device_t &device, _Object object) { return downcast<hp_taco_device &>(device).m_sts_handler.set_callback(object); }
|
||||
|
||||
// Register read/write
|
||||
DECLARE_WRITE16_MEMBER(reg_w);
|
||||
DECLARE_READ16_MEMBER(reg_r);
|
||||
|
||||
// Flag & status read
|
||||
DECLARE_READ_LINE_MEMBER(flg_r);
|
||||
DECLARE_READ_LINE_MEMBER(sts_r);
|
||||
|
||||
typedef UINT32 tape_pos_t;
|
||||
|
||||
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_irq_handler;
|
||||
devcb_write_line m_flg_handler;
|
||||
devcb_write_line m_sts_handler;
|
||||
|
||||
// Registers
|
||||
UINT16 m_data_reg;
|
||||
UINT16 m_cmd_reg;
|
||||
UINT16 m_status_reg;
|
||||
UINT16 m_tach_reg;
|
||||
UINT16 m_checksum_reg;
|
||||
UINT16 m_timing_reg;
|
||||
|
||||
// State
|
||||
bool m_irq;
|
||||
bool m_flg;
|
||||
bool m_sts;
|
||||
|
||||
// Tape position
|
||||
tape_pos_t m_tape_pos;
|
||||
attotime m_start_time;
|
||||
|
||||
// Timers
|
||||
emu_timer *m_tape_timer;
|
||||
|
||||
void irq_w(bool state);
|
||||
void set_error(bool state);
|
||||
bool check_for_errors(void);
|
||||
static unsigned speed_to_tick_freq(bool fast);
|
||||
void update_tape_pos(void);
|
||||
static bool any_hole(UINT32 tape_pos_a , UINT32 tape_pos_b);
|
||||
UINT32 next_hole(bool fwd) const;
|
||||
static attotime time_to_distance(UINT32 distance, bool fast);
|
||||
attotime time_to_target(UINT32 target, bool fast) const;
|
||||
void start_tape(void);
|
||||
void stop_tape(void);
|
||||
void start_cmd_exec(UINT16 new_cmd_reg);
|
||||
};
|
||||
|
||||
// device type definition
|
||||
extern const device_type HP_TACO;
|
||||
|
||||
#endif /* __HP_TACO_H__ */
|
@ -33,6 +33,7 @@
|
||||
#include "cpu/z80/z80.h"
|
||||
#include "softlist.h"
|
||||
#include "cpu/hphybrid/hphybrid.h"
|
||||
#include "machine/hp_taco.h"
|
||||
|
||||
#define BIT_MASK(n) (1U << (n))
|
||||
|
||||
@ -77,7 +78,8 @@ public:
|
||||
m_io_key0(*this , "KEY0"),
|
||||
m_io_key1(*this , "KEY1"),
|
||||
m_io_key2(*this , "KEY2"),
|
||||
m_io_key3(*this , "KEY3")
|
||||
m_io_key3(*this , "KEY3"),
|
||||
m_t15(*this , "t15")
|
||||
{ }
|
||||
|
||||
UINT32 screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect);
|
||||
@ -90,7 +92,7 @@ public:
|
||||
void vblank_w(screen_device &screen, bool state);
|
||||
|
||||
IRQ_CALLBACK_MEMBER(irq_callback);
|
||||
void update_irl(void);
|
||||
void update_irq(void);
|
||||
|
||||
TIMER_DEVICE_CALLBACK_MEMBER(kb_scan);
|
||||
DECLARE_READ16_MEMBER(kb_scancode_r);
|
||||
@ -99,6 +101,10 @@ public:
|
||||
|
||||
DECLARE_WRITE8_MEMBER(pa_w);
|
||||
|
||||
DECLARE_WRITE_LINE_MEMBER(t15_irq_w);
|
||||
DECLARE_WRITE_LINE_MEMBER(t15_flg_w);
|
||||
DECLARE_WRITE_LINE_MEMBER(t15_sts_w);
|
||||
|
||||
private:
|
||||
required_device<hp_5061_3001_cpu_device> m_lpu;
|
||||
required_device<hp_5061_3001_cpu_device> m_ppu;
|
||||
@ -107,6 +113,7 @@ private:
|
||||
required_ioport m_io_key1;
|
||||
required_ioport m_io_key2;
|
||||
required_ioport m_io_key3;
|
||||
required_device<hp_taco_device> m_t15;
|
||||
|
||||
void set_video_mar(UINT16 mar);
|
||||
void video_fill_buff(bool buff_idx);
|
||||
@ -136,12 +143,15 @@ private:
|
||||
|
||||
// Interrupt handling
|
||||
UINT8 m_irl_pending;
|
||||
UINT8 m_irh_pending;
|
||||
|
||||
// State of keyboard
|
||||
ioport_value m_kb_state[ 4 ];
|
||||
UINT8 m_kb_scancode;
|
||||
UINT16 m_kb_status;
|
||||
|
||||
// State of PPU I/O
|
||||
UINT8 m_ppu_pa;
|
||||
};
|
||||
|
||||
static INPUT_PORTS_START(hp9845b)
|
||||
@ -322,10 +332,13 @@ void hp9845b_state::machine_reset()
|
||||
m_video_frame = 0;
|
||||
|
||||
m_irl_pending = 0;
|
||||
m_irh_pending = 0;
|
||||
|
||||
memset(&m_kb_state[ 0 ] , 0 , sizeof(m_kb_state));
|
||||
m_kb_scancode = 0x7f;
|
||||
m_kb_status = 0;
|
||||
|
||||
m_ppu_pa = 0;
|
||||
}
|
||||
|
||||
void hp9845b_state::set_video_mar(UINT16 mar)
|
||||
@ -479,13 +492,14 @@ IRQ_CALLBACK_MEMBER(hp9845b_state::irq_callback)
|
||||
if (irqline == HPHYBRID_IRL) {
|
||||
return m_irl_pending;
|
||||
} else {
|
||||
return 0;
|
||||
return m_irh_pending;
|
||||
}
|
||||
}
|
||||
|
||||
void hp9845b_state::update_irl(void)
|
||||
void hp9845b_state::update_irq(void)
|
||||
{
|
||||
m_ppu->set_input_line(HPHYBRID_IRL , m_irl_pending != 0);
|
||||
m_ppu->set_input_line(HPHYBRID_IRH , m_irh_pending != 0);
|
||||
}
|
||||
|
||||
TIMER_DEVICE_CALLBACK_MEMBER(hp9845b_state::kb_scan)
|
||||
@ -546,7 +560,7 @@ TIMER_DEVICE_CALLBACK_MEMBER(hp9845b_state::kb_scan)
|
||||
m_kb_scancode = i;
|
||||
BIT_SET(m_irl_pending , 0);
|
||||
BIT_SET(m_kb_status, 0);
|
||||
update_irl();
|
||||
update_irq();
|
||||
|
||||
// Special case: pressing stop key sets LPU "status" flag
|
||||
if (i == 0x47) {
|
||||
@ -572,24 +586,50 @@ WRITE16_MEMBER(hp9845b_state::kb_irq_clear_w)
|
||||
{
|
||||
BIT_CLR(m_irl_pending , 0);
|
||||
BIT_CLR(m_kb_status, 0);
|
||||
update_irl();
|
||||
update_irq();
|
||||
m_lpu->status_w(0);
|
||||
// TODO: beeper start
|
||||
}
|
||||
|
||||
WRITE8_MEMBER(hp9845b_state::pa_w)
|
||||
{
|
||||
m_ppu_pa = data;
|
||||
|
||||
// TODO: handle sts & flg
|
||||
if (data == 0xf) {
|
||||
// RHS tape drive (T15)
|
||||
m_ppu->status_w(1);
|
||||
m_ppu->flag_w(1);
|
||||
if (data == 15) {
|
||||
// RHS tape drive (T15)
|
||||
m_ppu->status_w(m_t15->sts_r());
|
||||
m_ppu->flag_w(m_t15->flg_r());
|
||||
} else {
|
||||
m_ppu->status_w(0);
|
||||
m_ppu->flag_w(0);
|
||||
}
|
||||
}
|
||||
|
||||
WRITE_LINE_MEMBER(hp9845b_state::t15_irq_w)
|
||||
{
|
||||
if (state) {
|
||||
BIT_SET(m_irh_pending , 7);
|
||||
} else {
|
||||
BIT_CLR(m_irh_pending , 7);
|
||||
}
|
||||
update_irq();
|
||||
}
|
||||
|
||||
WRITE_LINE_MEMBER(hp9845b_state::t15_flg_w)
|
||||
{
|
||||
if (m_ppu_pa == 15) {
|
||||
m_ppu->flag_w(state);
|
||||
}
|
||||
}
|
||||
|
||||
WRITE_LINE_MEMBER(hp9845b_state::t15_sts_w)
|
||||
{
|
||||
if (m_ppu_pa == 15) {
|
||||
m_ppu->status_w(state);
|
||||
}
|
||||
}
|
||||
|
||||
static MACHINE_CONFIG_START( hp9845a, hp9845_state )
|
||||
//MCFG_CPU_ADD("lpu", HP_5061_3010, XTAL_11_4MHz)
|
||||
//MCFG_CPU_ADD("ppu", HP_5061_3011, XTAL_11_4MHz)
|
||||
@ -638,6 +678,9 @@ static ADDRESS_MAP_START(ppu_io_map , AS_IO , 16 , hp9845b_state)
|
||||
// PA = 0, IC = 3
|
||||
// Keyboard status input & keyboard interrupt clear
|
||||
AM_RANGE(HP_MAKE_IOADDR(0 , 3) , HP_MAKE_IOADDR(0 , 3)) AM_READWRITE(kb_status_r , kb_irq_clear_w)
|
||||
// PA = 15, IC = 0..3
|
||||
// Right-hand side tape drive (T15)
|
||||
AM_RANGE(HP_MAKE_IOADDR(15 , 0) , HP_MAKE_IOADDR(15 , 3)) AM_DEVREADWRITE("t15" , hp_taco_device , reg_r , reg_w)
|
||||
ADDRESS_MAP_END
|
||||
|
||||
static MACHINE_CONFIG_START( hp9845b, hp9845b_state )
|
||||
@ -663,6 +706,12 @@ static MACHINE_CONFIG_START( hp9845b, hp9845b_state )
|
||||
// Actual keyboard refresh rate should be KEY_SCAN_OSCILLATOR / 128 (2560 Hz)
|
||||
MCFG_TIMER_DRIVER_ADD_PERIODIC("kb_timer" , hp9845b_state , kb_scan , attotime::from_hz(100))
|
||||
|
||||
// Tape controller
|
||||
MCFG_DEVICE_ADD("t15" , HP_TACO , 4000000)
|
||||
MCFG_TACO_IRQ_HANDLER(WRITELINE(hp9845b_state , t15_irq_w))
|
||||
MCFG_TACO_FLG_HANDLER(WRITELINE(hp9845b_state , t15_flg_w))
|
||||
MCFG_TACO_STS_HANDLER(WRITELINE(hp9845b_state , t15_sts_w))
|
||||
|
||||
MCFG_SOFTWARE_LIST_ADD("optrom_list", "hp9845b_rom")
|
||||
MACHINE_CONFIG_END
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user