From 5dba426a7c96c1aad8a8f2b4660840681420b07c Mon Sep 17 00:00:00 2001 From: fulivi Date: Sun, 21 Oct 2018 18:15:57 +0200 Subject: [PATCH] HP9825: printer & beeper added (#4175) * hp9825: printer added. Whitespace cleanup on hp9825_tape.* * hp9825: added beeper --- src/mame/drivers/hp9825.cpp | 115 +++++++++++++++++++++++++++++-- src/mame/machine/hp9825_tape.cpp | 88 +++++++++++------------ src/mame/machine/hp9825_tape.h | 38 +++++----- 3 files changed, 174 insertions(+), 67 deletions(-) diff --git a/src/mame/drivers/hp9825.cpp b/src/mame/drivers/hp9825.cpp index b04967f41c1..e61a49f7177 100644 --- a/src/mame/drivers/hp9825.cpp +++ b/src/mame/drivers/hp9825.cpp @@ -14,12 +14,12 @@ // - Keyboard (SHIFT LOCK & RESET not implemented) // - Display & run light // - DC100 tape drive +// - Printer +// - Beeper // What's not yet in: // - Internal & external expansion ROMs // - Configurable RAM size -// - Printer // - I/O expansion slots: 98034 & 98035 modules from hp9845 emulation can be used here, too -// - Beeper // // 9825A & 9825T can also be emulated. At the moment I haven't all the necessary // ROM dumps, though. @@ -28,6 +28,9 @@ #include "cpu/hphybrid/hphybrid.h" #include "machine/timer.h" #include "machine/hp9825_tape.h" +#include "imagedev/bitbngr.h" +#include "speaker.h" +#include "sound/beep.h" #include "hp9825.lh" // CPU clock (generated by a trimmered RC oscillator) @@ -40,6 +43,15 @@ constexpr unsigned KDP_CLOCK = MAIN_CLOCK / 4; constexpr uint8_t KDP_PA = 0; constexpr uint8_t TAPE_PA = 1; +// KDP clocks to print 1 line of dots (~33 ms) +// This value is semi-guessed. +constexpr unsigned KDP_CLOCKS_PER_LINE = 50000; + +// Beeper constants +// Values come from R/C values on schematics +constexpr unsigned BEEPER_FREQ = 900; +constexpr unsigned BEEPER_MS = 40; + // Bit manipulation namespace { template constexpr T BIT_MASK(unsigned n) @@ -68,6 +80,11 @@ public: , m_tape(*this , "tape") , m_io_key(*this , "KEY%u" , 0) , m_shift_key(*this , "KEY_SHIFT") + , m_prt_alpha_out(*this , "prt_alpha") + , m_prt_graph_out(*this , "prt_graph") + , m_prt_timer(*this , "prt_timer") + , m_beeper(*this , "beeper") + , m_beep_timer(*this , "beep_timer") , m_display(*this , "char_%u_%u" , 0U , 0U) , m_run_light(*this , "run_light") { @@ -81,6 +98,11 @@ private: required_device m_tape; required_ioport_array<4> m_io_key; required_ioport m_shift_key; + required_device m_prt_alpha_out; + required_device m_prt_graph_out; + required_device m_prt_timer; + required_device m_beeper; + required_device m_beep_timer; output_finder<32 , 7> m_display; output_finder<> m_run_light; @@ -100,6 +122,10 @@ private: uint8_t m_pa; uint16_t m_flg_status; uint16_t m_sts_status; + // Printer + uint8_t m_printer_mem[ 16 ]; + uint8_t m_printer_idx; + unsigned m_printer_line; // 0: printer idle, 1..10: line being printed virtual void machine_start() override; virtual void machine_reset() override; @@ -111,6 +137,7 @@ private: DECLARE_WRITE16_MEMBER(disp_w); DECLARE_READ16_MEMBER(kdp_status_r); DECLARE_WRITE16_MEMBER(kdp_control_w); + DECLARE_WRITE16_MEMBER(printer_w); void update_display(); TIMER_DEVICE_CALLBACK_MEMBER(cursor_blink); @@ -125,6 +152,10 @@ private: void update_flg_sts(); void set_sts(uint8_t sc , int state); void set_flg(uint8_t sc , int state); + + TIMER_DEVICE_CALLBACK_MEMBER(prt_timer); + + TIMER_DEVICE_CALLBACK_MEMBER(beep_timer); }; void hp9825_state::machine_start() @@ -158,6 +189,11 @@ void hp9825_state::machine_reset() m_pa = 0; m_flg_status = 0; m_sts_status = 0; + m_printer_idx = 0; + m_printer_line = 0; + m_prt_timer->reset(); + m_beeper->set_state(0); + m_beep_timer->reset(); } void hp9825_state::cpu_io_map(address_map &map) @@ -165,6 +201,7 @@ void hp9825_state::cpu_io_map(address_map &map) map.unmap_value_low(); map(HP_MAKE_IOADDR(KDP_PA , 0) , HP_MAKE_IOADDR(KDP_PA , 0)).rw(FUNC(hp9825_state::kb_scancode_r) , FUNC(hp9825_state::disp_w)); map(HP_MAKE_IOADDR(KDP_PA , 1) , HP_MAKE_IOADDR(KDP_PA , 1)).rw(FUNC(hp9825_state::kdp_status_r) , FUNC(hp9825_state::kdp_control_w)); + map(HP_MAKE_IOADDR(KDP_PA , 2) , HP_MAKE_IOADDR(KDP_PA , 2)).w(FUNC(hp9825_state::printer_w)); map(HP_MAKE_IOADDR(TAPE_PA , 0) , HP_MAKE_IOADDR(TAPE_PA , 3)).rw(m_tape , FUNC(hp9825_tape_device::tape_r) , FUNC(hp9825_tape_device::tape_w)); // TODO: } @@ -202,8 +239,15 @@ WRITE16_MEMBER(hp9825_state::disp_w) READ16_MEMBER(hp9825_state::kdp_status_r) { - // TODO: - return 8; + uint16_t res = 8; + if (BIT(m_irl_pending, KDP_PA)) { + BIT_SET(res , 4); + } + if (m_printer_line) { + BIT_SET(res , 2); + } + + return res; } WRITE16_MEMBER(hp9825_state::kdp_control_w) @@ -234,11 +278,33 @@ WRITE16_MEMBER(hp9825_state::kdp_control_w) } else if (BIT(data , 3)) { m_run_light = true; } + if (BIT(data , 0) && m_printer_line == 0) { + // Start printing + // Dump text line to alpha bitbanger + for (auto c : m_printer_mem) { + m_prt_alpha_out->output(c); + } + m_prt_alpha_out->output('\n'); + m_printer_idx = 0; + m_printer_line++; + m_prt_timer->adjust(attotime::from_ticks(KDP_CLOCKS_PER_LINE , KDP_CLOCK)); + } + if (BIT(data , 2)) { + // Start beeper + m_beeper->set_state(1); + m_beep_timer->adjust(attotime::from_msec(BEEPER_MS)); + } if (regen_display) { update_display(); } } +WRITE16_MEMBER(hp9825_state::printer_w) +{ + m_printer_mem[ m_printer_idx ] = uint8_t(data); + m_printer_idx = (m_printer_idx + 1) & 0xf; +} + // The character generator was reverse engineered from images of printer & display test patterns. // It is not guaranteed to be pixel-accurate though it looks quite close to the original. static const uint8_t chargen[ 128 ][ 5 ] = { @@ -546,6 +612,37 @@ void hp9825_state::set_flg(uint8_t sc , int state) } } +TIMER_DEVICE_CALLBACK_MEMBER(hp9825_state::prt_timer) +{ + if (m_printer_line == 1 || m_printer_line == 9 || m_printer_line == 10) { + // Empty lines + for (unsigned i = 0; i < 110; i++) { + m_prt_graph_out->output(' '); + } + } else { + for (unsigned i = 0; i < 16; i++) { + for (unsigned col = 0; col < 5; col++) { + uint8_t pixels = chargen[ m_printer_mem[ i ] & 0x7f ][ col ]; + m_prt_graph_out->output(BIT(pixels , m_printer_line - 2) ? '*' : ' '); + } + m_prt_graph_out->output(' '); + m_prt_graph_out->output(' '); + } + } + m_prt_graph_out->output('\n'); + m_printer_line++; + if (m_printer_line <= 10) { + m_prt_timer->adjust(attotime::from_ticks(KDP_CLOCKS_PER_LINE , KDP_CLOCK)); + } else { + m_printer_line = 0; + } +} + +TIMER_DEVICE_CALLBACK_MEMBER(hp9825_state::beep_timer) +{ + m_beeper->set_state(0); +} + MACHINE_CONFIG_START(hp9825_state::hp9825b) HP_09825_67907(config , m_cpu , MAIN_CLOCK); // Just guessing... settings borrowed from hp9845 @@ -567,6 +664,16 @@ MACHINE_CONFIG_START(hp9825_state::hp9825b) m_tape->sts().set([this , sc = TAPE_PA](int state) { set_sts(sc , state); }); m_tape->dmar().set(m_cpu , FUNC(hp_09825_67907_cpu_device::dmar_w)); + // Printer + BITBANGER(config , m_prt_alpha_out , 0); + BITBANGER(config , m_prt_graph_out , 0); + TIMER(config , m_prt_timer , 0).configure_generic(timer_device::expired_delegate(FUNC(hp9825_state::prt_timer) , this)); + + // Beeper + SPEAKER(config, "mono").front_center(); + BEEP(config, m_beeper, BEEPER_FREQ).add_route(ALL_OUTPUTS, "mono", 1.00); + TIMER(config , m_beep_timer , 0).configure_generic(timer_device::expired_delegate(FUNC(hp9825_state::beep_timer) , this)); + config.set_default_layout(layout_hp9825); MACHINE_CONFIG_END diff --git a/src/mame/machine/hp9825_tape.cpp b/src/mame/machine/hp9825_tape.cpp index 63234e2f3aa..d53b75652c5 100644 --- a/src/mame/machine/hp9825_tape.cpp +++ b/src/mame/machine/hp9825_tape.cpp @@ -42,36 +42,36 @@ 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 +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 enum : unsigned { - CMD_REG_MOTOR_BIT = 7, // Motor on (0) - CMD_REG_WR_GATE_BIT = 6, // Write gate (0) - CMD_REG_SPEED_BIT = 5, // Tape speed (1 = slow) - CMD_REG_DIR_BIT = 4, // Tape direction (1 = fwd) - CMD_REG_FLG_SEL_BIT = 3, // FLG selection (0 = tacho pulses, 1 = bit clock) - CMD_REG_THRESHOLD_BIT = 2, // Threshold selection - CMD_REG_DMA_EN_BIT = 1, // DMA enable (0) - CMD_REG_TRACK_SEL_BIT = 0 // Track selection (1 = A) + CMD_REG_MOTOR_BIT = 7, // Motor on (0) + CMD_REG_WR_GATE_BIT = 6, // Write gate (0) + CMD_REG_SPEED_BIT = 5, // Tape speed (1 = slow) + CMD_REG_DIR_BIT = 4, // Tape direction (1 = fwd) + CMD_REG_FLG_SEL_BIT = 3, // FLG selection (0 = tacho pulses, 1 = bit clock) + CMD_REG_THRESHOLD_BIT = 2, // Threshold selection + CMD_REG_DMA_EN_BIT = 1, // DMA enable (0) + CMD_REG_TRACK_SEL_BIT = 0 // Track selection (1 = A) }; // Bits in status register enum : unsigned { - STAT_REG_WPR_BIT = 7, // Write protected (1) - STAT_REG_DIR_BIT = 6, // Tape direction (1 = rev) - STAT_REG_MVG_BIT = 5, // Tape moving (1) - STAT_REG_GAP_BIT = 4, // Gap (1) or data (0) - STAT_REG_COUT_BIT = 2, // Cartridge out (1) - STAT_REG_SVF_BIT = 1, // Servo failure (1) - STAT_REG_EOT_BIT = 0 // End of tape (1) + STAT_REG_WPR_BIT = 7, // Write protected (1) + STAT_REG_DIR_BIT = 6, // Tape direction (1 = rev) + STAT_REG_MVG_BIT = 5, // Tape moving (1) + STAT_REG_GAP_BIT = 4, // Gap (1) or data (0) + STAT_REG_COUT_BIT = 2, // Cartridge out (1) + STAT_REG_SVF_BIT = 1, // Servo failure (1) + STAT_REG_EOT_BIT = 0 // End of tape (1) }; // Timers @@ -100,19 +100,19 @@ hp9825_tape_device::hp9825_tape_device(const machine_config &mconfig, const char } MACHINE_CONFIG_START(hp9825_tape_device::device_add_mconfig) - 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)); - m_short_gap_timer->set_capacitor_value(CAP_N(10)); - m_short_gap_timer->set_a_pin_value(0); - m_short_gap_timer->set_clear_pin_value(1); + 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)); + m_short_gap_timer->set_capacitor_value(CAP_N(10)); + m_short_gap_timer->set_a_pin_value(0); + m_short_gap_timer->set_clear_pin_value(1); m_short_gap_timer->out_cb().set(FUNC(hp9825_tape_device::short_gap_w)); TTL74123(config , m_long_gap_timer , 0); - m_long_gap_timer->set_connection_type(TTL74123_NOT_GROUNDED_NO_DIODE); - m_long_gap_timer->set_resistor_value(RES_K(28.7)); - m_long_gap_timer->set_capacitor_value(CAP_U(0.22)); - m_long_gap_timer->set_clear_pin_value(1); + m_long_gap_timer->set_connection_type(TTL74123_NOT_GROUNDED_NO_DIODE); + m_long_gap_timer->set_resistor_value(RES_K(28.7)); + m_long_gap_timer->set_capacitor_value(CAP_U(0.22)); + m_long_gap_timer->set_clear_pin_value(1); m_long_gap_timer->out_cb().set(FUNC(hp9825_tape_device::long_gap_w)); MACHINE_CONFIG_END @@ -127,7 +127,7 @@ void hp9825_tape_device::device_start() 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)); @@ -196,7 +196,7 @@ 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(); @@ -210,7 +210,7 @@ void hp9825_tape_device::device_timer(emu_timer &timer, device_timer_id id, int { 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; @@ -270,7 +270,7 @@ void hp9825_tape_device::device_timer(emu_timer &timer, device_timer_id id, int case INV_TMR_ID: // In itself it does nothing (all work is in update_speed_pos) break; - + default: break; } @@ -280,7 +280,7 @@ void hp9825_tape_device::device_timer(emu_timer &timer, device_timer_id id, int image_init_result hp9825_tape_device::internal_load(bool is_create) { LOG("load %d\n" , is_create); - + device_reset(); io_generic io; @@ -335,7 +335,7 @@ void hp9825_tape_device::call_unload() std::string hp9825_tape_device::call_display() { - // TODO: + // TODO: return std::string(); } @@ -347,7 +347,7 @@ const char *hp9825_tape_device::file_extensions() const READ16_MEMBER(hp9825_tape_device::tape_r) { uint16_t res = 0; - + switch (offset) { case 0: // R4: read data out @@ -495,7 +495,7 @@ void hp9825_tape_device::update_sts() // m_in_gap auto prev_set_point = get_speed_set_point(); auto prev_exception = m_exception; - + m_exception = BIT(m_stat_reg , STAT_REG_EOT_BIT) || BIT(m_stat_reg , STAT_REG_COUT_BIT) || @@ -689,7 +689,7 @@ void hp9825_tape_device::update_speed_pos() 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)) { @@ -731,7 +731,7 @@ void hp9825_tape_device::time_to_distance(hti_format_t::tape_pos_t distance , ht 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; @@ -918,7 +918,7 @@ void hp9825_tape_device::rd_bit(bool bit) m_in_gap = false; m_long_gap_timer->a_w(m_in_gap); update_sts(); - } + } } void hp9825_tape_device::wr_bit(bool bit) diff --git a/src/mame/machine/hp9825_tape.h b/src/mame/machine/hp9825_tape.h index 48accb2e88f..c2e13ef1898 100644 --- a/src/mame/machine/hp9825_tape.h +++ b/src/mame/machine/hp9825_tape.h @@ -27,7 +27,7 @@ public: 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; @@ -51,16 +51,16 @@ public: DECLARE_WRITE_LINE_MEMBER(short_gap_w); DECLARE_WRITE_LINE_MEMBER(long_gap_w); - + 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 m_short_gap_timer; // U43a - required_device m_long_gap_timer; // U43b - + required_device m_short_gap_timer; // U43a + required_device m_long_gap_timer; // U43b + // Registers uint8_t m_cmd_reg; uint8_t m_stat_reg; @@ -68,18 +68,18 @@ private: // State bool m_flg; bool m_sts; - bool m_data_out; // U38-9 - bool m_data_in; // U13-6 - bool m_exception; // U4-6 - bool m_search_complete; // U9-6 - bool m_dma_req; // U9-9 - bool m_in_gap; // U39-4 - bool m_no_go; // U6-3 + bool m_data_out; // U38-9 + bool m_data_in; // U13-6 + bool m_exception; // U4-6 + bool m_search_complete; // U9-6 + 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 + 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; @@ -97,7 +97,7 @@ private: 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 + attotime m_start_time; // Tape moving if != never bool m_accelerating; // R/W @@ -111,8 +111,8 @@ private: 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" + 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();