From 408c6351cf0b34d2d1ab518451d1239429c08cee Mon Sep 17 00:00:00 2001 From: fulivi Date: Mon, 11 Jan 2016 13:26:13 +0100 Subject: [PATCH] hp9845: initial version of HP TACO driver (only basic tape movement is working) --- scripts/src/machine.lua | 12 + scripts/target/mame/mess.lua | 1 + src/devices/machine/hp_taco.cpp | 516 ++++++++++++++++++++++++++++++++ src/devices/machine/hp_taco.h | 93 ++++++ src/mame/drivers/hp9845.cpp | 69 ++++- 5 files changed, 681 insertions(+), 10 deletions(-) create mode 100644 src/devices/machine/hp_taco.cpp create mode 100644 src/devices/machine/hp_taco.h diff --git a/scripts/src/machine.lua b/scripts/src/machine.lua index 5a97d22d015..fbc923b3073 100644 --- a/scripts/src/machine.lua +++ b/scripts/src/machine.lua @@ -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 diff --git a/scripts/target/mame/mess.lua b/scripts/target/mame/mess.lua index cb9a150224a..80be3e7c76c 100644 --- a/scripts/target/mame/mess.lua +++ b/scripts/target/mame/mess.lua @@ -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 diff --git a/src/devices/machine/hp_taco.cpp b/src/devices/machine/hp_taco.cpp new file mode 100644 index 00000000000..1597c5c5733 --- /dev/null +++ b/src/devices/machine/hp_taco.cpp @@ -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; + +// 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; +} diff --git a/src/devices/machine/hp_taco.h b/src/devices/machine/hp_taco.h new file mode 100644 index 00000000000..e279d045d10 --- /dev/null +++ b/src/devices/machine/hp_taco.h @@ -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 static devcb_base &set_irq_handler(device_t &device, _Object object) { return downcast(device).m_irq_handler.set_callback(object); } + template static devcb_base &set_flg_handler(device_t &device, _Object object) { return downcast(device).m_flg_handler.set_callback(object); } + template static devcb_base &set_sts_handler(device_t &device, _Object object) { return downcast(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__ */ diff --git a/src/mame/drivers/hp9845.cpp b/src/mame/drivers/hp9845.cpp index 2936cae3228..ec0e53da906 100644 --- a/src/mame/drivers/hp9845.cpp +++ b/src/mame/drivers/hp9845.cpp @@ -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 m_lpu; required_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 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