summaryrefslogtreecommitdiffstatshomepage
path: root/src/mame/drivers/hp9825.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/mame/drivers/hp9825.cpp')
-rw-r--r--src/mame/drivers/hp9825.cpp115
1 files changed, 111 insertions, 4 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<typename T> 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<hp9825_tape_device> m_tape;
required_ioport_array<4> m_io_key;
required_ioport m_shift_key;
+ required_device<bitbanger_device> m_prt_alpha_out;
+ required_device<bitbanger_device> m_prt_graph_out;
+ required_device<timer_device> m_prt_timer;
+ required_device<beep_device> m_beeper;
+ required_device<timer_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