From e901eadaf3f33d41f9f62f057d115be70176490d Mon Sep 17 00:00:00 2001 From: Ramiro Polla Date: Fri, 21 Nov 2014 23:54:10 +0100 Subject: [PATCH 1/3] lx810l: remove unnecessary beeper --- src/emu/bus/centronics/epson_lx810l.c | 1 - src/emu/bus/centronics/epson_lx810l.h | 1 - 2 files changed, 2 deletions(-) diff --git a/src/emu/bus/centronics/epson_lx810l.c b/src/emu/bus/centronics/epson_lx810l.c index 2882e7cfbc8..1dad2d8dbee 100644 --- a/src/emu/bus/centronics/epson_lx810l.c +++ b/src/emu/bus/centronics/epson_lx810l.c @@ -140,7 +140,6 @@ static MACHINE_CONFIG_FRAGMENT( epson_lx810l ) /* audio hardware */ MCFG_SPEAKER_STANDARD_MONO("mono") - MCFG_SOUND_ADD("beeper", BEEP, 0) MCFG_SOUND_ADD("speaker", SPEAKER_SOUND, 0) MCFG_SOUND_ROUTE(ALL_OUTPUTS, "mono", 0.25) diff --git a/src/emu/bus/centronics/epson_lx810l.h b/src/emu/bus/centronics/epson_lx810l.h index 29f80e803e4..d754f72be40 100644 --- a/src/emu/bus/centronics/epson_lx810l.h +++ b/src/emu/bus/centronics/epson_lx810l.h @@ -17,7 +17,6 @@ #include "machine/e05a30.h" #include "machine/eepromser.h" #include "machine/steppers.h" -#include "sound/beep.h" #include "sound/speaker.h" From ada21be3f22a857a2d7039e5faa764a59bbf35bc Mon Sep 17 00:00:00 2001 From: Ramiro Polla Date: Sun, 23 Nov 2014 00:59:00 +0100 Subject: [PATCH 2/3] lx810l: implement centronics input --- src/emu/bus/centronics/epson_lx810l.c | 16 +++++-- src/emu/bus/centronics/epson_lx810l.h | 17 ++++++++ src/emu/machine/e05a30.c | 62 +++++++++++++++++++++++++++ src/emu/machine/e05a30.h | 44 +++++++++++++++++++ 4 files changed, 136 insertions(+), 3 deletions(-) diff --git a/src/emu/bus/centronics/epson_lx810l.c b/src/emu/bus/centronics/epson_lx810l.c index 1dad2d8dbee..dca8a4277c5 100644 --- a/src/emu/bus/centronics/epson_lx810l.c +++ b/src/emu/bus/centronics/epson_lx810l.c @@ -13,7 +13,10 @@ * SLA7020M (step motor driver) * uPC494C (pulse width modulation control) * - * Devices boot and enter main input loop, but input is not yet implemented. + * Devices boot and enter main input loop. Data is received through the + * centronics bus and printed as expected. The actual paper output is + * still not implemented, though. Look at the output from the fire signal + * (epson_lx810l_t::co0_w()) to see what's actually being printed. * * It is possible to run the printers' self test with this procedure: * - Turn on device; @@ -99,7 +102,7 @@ static ADDRESS_MAP_START( lx810l_mem, AS_PROGRAM, 8, epson_lx810l_t ) AM_RANGE(0x0000, 0x7fff) AM_ROM /* 32k firmware */ AM_RANGE(0x8000, 0x9fff) AM_RAM /* 8k external RAM */ AM_RANGE(0xa000, 0xbfff) AM_READWRITE(fakemem_r, fakemem_w) /* fake memory, write one, set all */ - AM_RANGE(0xc000, 0xdfff) AM_MIRROR(0x1ff0) AM_DEVREADWRITE("ic3b", e05a30_device, read, write) + AM_RANGE(0xc000, 0xdfff) AM_MIRROR(0x1ff0) AM_DEVREADWRITE("e05a30", e05a30_device, read, write) AM_RANGE(0xe000, 0xfeff) AM_NOP /* not used */ AM_RANGE(0xff00, 0xffff) AM_RAM /* internal CPU RAM */ ADDRESS_MAP_END @@ -144,11 +147,16 @@ static MACHINE_CONFIG_FRAGMENT( epson_lx810l ) MCFG_SOUND_ROUTE(ALL_OUTPUTS, "mono", 0.25) /* gate array */ - MCFG_DEVICE_ADD("ic3b", E05A30, 0) + MCFG_DEVICE_ADD("e05a30", E05A30, 0) MCFG_E05A30_PRINTHEAD_CALLBACK(WRITE16(epson_lx810l_t, printhead)) MCFG_E05A30_PF_STEPPER_CALLBACK(WRITE8(epson_lx810l_t, pf_stepper)) MCFG_E05A30_CR_STEPPER_CALLBACK(WRITE8(epson_lx810l_t, cr_stepper)) MCFG_E05A30_READY_CALLBACK(WRITELINE(epson_lx810l_t, e05a30_ready)) + MCFG_E05A30_CENTRONICS_ACK_CALLBACK(WRITELINE(epson_lx810l_t, e05a30_centronics_ack)) + MCFG_E05A30_CENTRONICS_BUSY_CALLBACK(WRITELINE(epson_lx810l_t, e05a30_centronics_busy)) + MCFG_E05A30_CENTRONICS_PERROR_CALLBACK(WRITELINE(epson_lx810l_t, e05a30_centronics_perror)) + MCFG_E05A30_CENTRONICS_FAULT_CALLBACK(WRITELINE(epson_lx810l_t, e05a30_centronics_fault)) + MCFG_E05A30_CENTRONICS_SELECT_CALLBACK(WRITELINE(epson_lx810l_t, e05a30_centronics_select)) /* 256-bit eeprom */ MCFG_EEPROM_SERIAL_93C06_ADD("eeprom") @@ -272,6 +280,7 @@ epson_lx810l_t::epson_lx810l_t(const machine_config &mconfig, const char *tag, d m_maincpu(*this, "maincpu"), m_eeprom(*this, "eeprom"), m_speaker(*this, "speaker"), + m_e05a30(*this, "e05a30"), m_93c06_clk(0), m_93c06_cs(0), m_printhead(0), @@ -287,6 +296,7 @@ epson_lx810l_t::epson_lx810l_t(const machine_config &mconfig, device_type type, m_maincpu(*this, "maincpu"), m_eeprom(*this, "eeprom"), m_speaker(*this, "speaker"), + m_e05a30(*this, "e05a30"), m_93c06_clk(0), m_93c06_cs(0), m_printhead(0), diff --git a/src/emu/bus/centronics/epson_lx810l.h b/src/emu/bus/centronics/epson_lx810l.h index d754f72be40..8ff835facba 100644 --- a/src/emu/bus/centronics/epson_lx810l.h +++ b/src/emu/bus/centronics/epson_lx810l.h @@ -73,6 +73,22 @@ public: DECLARE_WRITE8_MEMBER(cr_stepper); DECLARE_WRITE_LINE_MEMBER(e05a30_ready); + /* Centronics stuff */ + virtual DECLARE_WRITE_LINE_MEMBER( input_strobe ) { if (m_e05a30) m_e05a30->centronics_input_strobe(state); } + virtual DECLARE_WRITE_LINE_MEMBER( input_data0 ) { if (m_e05a30) m_e05a30->centronics_input_data0(state); } + virtual DECLARE_WRITE_LINE_MEMBER( input_data1 ) { if (m_e05a30) m_e05a30->centronics_input_data1(state); } + virtual DECLARE_WRITE_LINE_MEMBER( input_data2 ) { if (m_e05a30) m_e05a30->centronics_input_data2(state); } + virtual DECLARE_WRITE_LINE_MEMBER( input_data3 ) { if (m_e05a30) m_e05a30->centronics_input_data3(state); } + virtual DECLARE_WRITE_LINE_MEMBER( input_data4 ) { if (m_e05a30) m_e05a30->centronics_input_data4(state); } + virtual DECLARE_WRITE_LINE_MEMBER( input_data5 ) { if (m_e05a30) m_e05a30->centronics_input_data5(state); } + virtual DECLARE_WRITE_LINE_MEMBER( input_data6 ) { if (m_e05a30) m_e05a30->centronics_input_data6(state); } + virtual DECLARE_WRITE_LINE_MEMBER( input_data7 ) { if (m_e05a30) m_e05a30->centronics_input_data7(state); } + DECLARE_WRITE_LINE_MEMBER(e05a30_centronics_ack) { output_ack(state); } + DECLARE_WRITE_LINE_MEMBER(e05a30_centronics_busy) { output_busy(state); } + DECLARE_WRITE_LINE_MEMBER(e05a30_centronics_perror) { output_perror(state); } + DECLARE_WRITE_LINE_MEMBER(e05a30_centronics_fault) { output_fault(state); } + DECLARE_WRITE_LINE_MEMBER(e05a30_centronics_select) { output_select(state); } + /* Panel buttons */ DECLARE_INPUT_CHANGED_MEMBER(online_sw); @@ -85,6 +101,7 @@ private: required_device m_maincpu; required_device m_eeprom; required_device m_speaker; + required_device m_e05a30; int m_93c06_clk; int m_93c06_cs; diff --git a/src/emu/machine/e05a30.c b/src/emu/machine/e05a30.c index d080da38006..5e3de355336 100644 --- a/src/emu/machine/e05a30.c +++ b/src/emu/machine/e05a30.c @@ -28,6 +28,11 @@ e05a30_device::e05a30_device(const machine_config &mconfig, const char *tag, dev m_write_pf_stepper(*this), m_write_cr_stepper(*this), m_write_ready(*this), + m_write_centronics_ack(*this), + m_write_centronics_busy(*this), + m_write_centronics_perror(*this), + m_write_centronics_fault(*this), + m_write_centronics_select(*this), m_printhead(0), m_pf_stepper(0), m_cr_stepper(0) @@ -45,6 +50,11 @@ void e05a30_device::device_start() m_write_pf_stepper.resolve_safe(); m_write_cr_stepper.resolve_safe(); m_write_ready.resolve_safe(); + m_write_centronics_ack.resolve_safe(); + m_write_centronics_busy.resolve_safe(); + m_write_centronics_perror.resolve_safe(); + m_write_centronics_fault.resolve_safe(); + m_write_centronics_select.resolve_safe(); /* register for state saving */ save_item(NAME(m_printhead)); @@ -62,6 +72,15 @@ void e05a30_device::device_reset() m_pf_stepper = 0x00; m_cr_stepper = 0x00; + /* centronics init */ + m_centronics_nack = FALSE; + m_centronics_busy = FALSE; + m_write_centronics_ack (!m_centronics_nack); + m_write_centronics_busy ( m_centronics_busy); + m_write_centronics_perror(FALSE); + m_write_centronics_fault (TRUE); + m_write_centronics_select(TRUE); + m_write_ready(1); } @@ -125,6 +144,25 @@ void e05a30_device::update_cr_stepper(UINT8 data) } +/*************************************************************************** + Centronics +***************************************************************************/ + +WRITE_LINE_MEMBER( e05a30_device::centronics_input_strobe ) +{ + if (m_centronics_strobe == TRUE && state == FALSE && !m_centronics_busy) { + + m_centronics_data_latch = m_centronics_data; + + m_centronics_data_latched = TRUE; + m_centronics_busy = TRUE; + m_write_centronics_busy(m_centronics_busy); + } + + m_centronics_strobe = state; +} + + /*************************************************************************** IMPLEMENTATION ***************************************************************************/ @@ -134,6 +172,19 @@ WRITE8_MEMBER( e05a30_device::write ) LOG("%s: e05a30_w([0xC0%02x]): %02x\n", space.machine().describe_context(), offset, data); switch (offset) { + case 0x04: + m_centronics_nack = BIT(data,5); + m_centronics_busy = BIT(data,0); + /* The ActionPrinter 2000 firmware might overwrite the busy signal at + * address 20AB if the host depends only on the busy signal and + * doesn't wait for the ack pulse. To avoid skipping input data, we + * assume the busy signal cannot be reset while the data hasn't been + * read. */ + if (m_centronics_data_latched) + m_centronics_busy = TRUE; + m_write_centronics_ack (!m_centronics_nack); + m_write_centronics_busy( m_centronics_busy); + break; /* printhead */ case 0x05: update_printhead(0, data); break; case 0x06: update_printhead(1, data); break; @@ -151,6 +202,17 @@ READ8_MEMBER( e05a30_device::read ) LOG("%s: e05a30_r([0xC0%02x]): ", space.machine().describe_context(), offset); switch (offset) { + case 0x02: + result = m_centronics_data_latched << 7; + break; + case 0x03: + result = m_centronics_data_latch; + m_centronics_data_latched = FALSE; + break; + case 0x04: + result |= m_centronics_busy << 0; + result |= m_centronics_nack << 5; + break; /* paper feed stepper motor */ case 0x07: result = m_pf_stepper; break; /* carriage return stepper motor */ diff --git a/src/emu/machine/e05a30.h b/src/emu/machine/e05a30.h index ab32eb21314..cc1ab8eb9da 100644 --- a/src/emu/machine/e05a30.h +++ b/src/emu/machine/e05a30.h @@ -24,6 +24,21 @@ #define MCFG_E05A30_READY_CALLBACK(_write) \ devcb = &e05a30_device::set_ready_wr_callback(*device, DEVCB_##_write); +#define MCFG_E05A30_CENTRONICS_ACK_CALLBACK(_write) \ + devcb = &e05a30_device::set_centronics_ack_wr_callback(*device, DEVCB_##_write); + +#define MCFG_E05A30_CENTRONICS_BUSY_CALLBACK(_write) \ + devcb = &e05a30_device::set_centronics_busy_wr_callback(*device, DEVCB_##_write); + +#define MCFG_E05A30_CENTRONICS_PERROR_CALLBACK(_write) \ + devcb = &e05a30_device::set_centronics_perror_wr_callback(*device, DEVCB_##_write); + +#define MCFG_E05A30_CENTRONICS_FAULT_CALLBACK(_write) \ + devcb = &e05a30_device::set_centronics_fault_wr_callback(*device, DEVCB_##_write); + +#define MCFG_E05A30_CENTRONICS_SELECT_CALLBACK(_write) \ + devcb = &e05a30_device::set_centronics_select_wr_callback(*device, DEVCB_##_write); + /*************************************************************************** TYPE DEFINITIONS ***************************************************************************/ @@ -38,10 +53,26 @@ public: template static devcb_base &set_pf_stepper_wr_callback(device_t &device, _Object object) { return downcast(device).m_write_pf_stepper.set_callback(object); } template static devcb_base &set_cr_stepper_wr_callback(device_t &device, _Object object) { return downcast(device).m_write_cr_stepper.set_callback(object); } template static devcb_base &set_ready_wr_callback(device_t &device, _Object object) { return downcast(device).m_write_ready.set_callback(object); } + template static devcb_base &set_centronics_ack_wr_callback(device_t &device, _Object object) { return downcast(device).m_write_centronics_ack.set_callback(object); } + template static devcb_base &set_centronics_busy_wr_callback(device_t &device, _Object object) { return downcast(device).m_write_centronics_busy.set_callback(object); } + template static devcb_base &set_centronics_perror_wr_callback(device_t &device, _Object object) { return downcast(device).m_write_centronics_perror.set_callback(object); } + template static devcb_base &set_centronics_fault_wr_callback(device_t &device, _Object object) { return downcast(device).m_write_centronics_fault.set_callback(object); } + template static devcb_base &set_centronics_select_wr_callback(device_t &device, _Object object) { return downcast(device).m_write_centronics_select.set_callback(object); } DECLARE_WRITE8_MEMBER( write ); DECLARE_READ8_MEMBER( read ); + /* Centronics stuff */ + DECLARE_WRITE_LINE_MEMBER( centronics_input_strobe ); + DECLARE_WRITE_LINE_MEMBER( centronics_input_data0 ) { if (state) m_centronics_data |= 0x01; else m_centronics_data &= ~0x01; } + DECLARE_WRITE_LINE_MEMBER( centronics_input_data1 ) { if (state) m_centronics_data |= 0x02; else m_centronics_data &= ~0x02; } + DECLARE_WRITE_LINE_MEMBER( centronics_input_data2 ) { if (state) m_centronics_data |= 0x04; else m_centronics_data &= ~0x04; } + DECLARE_WRITE_LINE_MEMBER( centronics_input_data3 ) { if (state) m_centronics_data |= 0x08; else m_centronics_data &= ~0x08; } + DECLARE_WRITE_LINE_MEMBER( centronics_input_data4 ) { if (state) m_centronics_data |= 0x10; else m_centronics_data &= ~0x10; } + DECLARE_WRITE_LINE_MEMBER( centronics_input_data5 ) { if (state) m_centronics_data |= 0x20; else m_centronics_data &= ~0x20; } + DECLARE_WRITE_LINE_MEMBER( centronics_input_data6 ) { if (state) m_centronics_data |= 0x40; else m_centronics_data &= ~0x40; } + DECLARE_WRITE_LINE_MEMBER( centronics_input_data7 ) { if (state) m_centronics_data |= 0x80; else m_centronics_data &= ~0x80; } + protected: // device-level overrides virtual void device_start(); @@ -53,6 +84,11 @@ private: devcb_write8 m_write_pf_stepper; devcb_write8 m_write_cr_stepper; devcb_write_line m_write_ready; + devcb_write_line m_write_centronics_ack; + devcb_write_line m_write_centronics_busy; + devcb_write_line m_write_centronics_perror; + devcb_write_line m_write_centronics_fault; + devcb_write_line m_write_centronics_select; void update_printhead(int pos, UINT8 data); void update_pf_stepper(UINT8 data); @@ -64,6 +100,14 @@ private: UINT8 m_pf_stepper; /* port 0x08 (4-bit) */ UINT8 m_cr_stepper; + + /* Centronics stuff */ + UINT8 m_centronics_data; + int m_centronics_busy; + int m_centronics_nack; + UINT8 m_centronics_strobe; + UINT8 m_centronics_data_latch; + UINT8 m_centronics_data_latched; }; extern const device_type E05A30; From dee8180c71b82ecb50d34e6ef7efa3ed3c72ac7f Mon Sep 17 00:00:00 2001 From: Ramiro Polla Date: Tue, 25 Nov 2014 02:08:37 +0100 Subject: [PATCH 3/3] lx810l: more accurate pixel vertical alignment The timings of the carriage stepper motor were modelled to accurately describe the vertical position of the printhead at each fire signal. --- src/emu/bus/centronics/epson_lx810l.c | 74 +++++++++++++++++++-------- src/emu/bus/centronics/epson_lx810l.h | 9 +++- 2 files changed, 62 insertions(+), 21 deletions(-) diff --git a/src/emu/bus/centronics/epson_lx810l.c b/src/emu/bus/centronics/epson_lx810l.c index dca8a4277c5..94b24fc3f46 100644 --- a/src/emu/bus/centronics/epson_lx810l.c +++ b/src/emu/bus/centronics/epson_lx810l.c @@ -286,7 +286,9 @@ epson_lx810l_t::epson_lx810l_t(const machine_config &mconfig, const char *tag, d m_printhead(0), m_pf_pos_abs(200), m_cr_pos_abs(200), - m_last_fire(0) + m_real_cr_pos(200), + m_real_cr_steps(0), + m_real_cr_dir(0) { } @@ -302,7 +304,9 @@ epson_lx810l_t::epson_lx810l_t(const machine_config &mconfig, device_type type, m_printhead(0), m_pf_pos_abs(200), m_cr_pos_abs(200), - m_last_fire(0) + m_real_cr_pos(200), + m_real_cr_steps(0), + m_real_cr_dir(0) { } @@ -350,6 +354,29 @@ void epson_lx810l_t::device_reset() } +//------------------------------------------------- +// device_timer - device-specific timer +//------------------------------------------------- + +void epson_lx810l_t::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr) +{ + switch (id) { + case TIMER_CR: + /* The firmware issues two half-steps in sequence, one immediately + * after the other. At full speed, the motor does two half-steps at + * each 833 microseconds. A timer fires the printhead twice, with + * the same period as each half-step (417 microseconds), but with + * a 356 microseconds delay relative to the motor steps. + */ + m_real_cr_pos += param; + m_real_cr_steps--; + if (m_real_cr_steps) + timer_set(attotime::from_usec(400), TIMER_CR, m_real_cr_dir); + break; + } +} + + /*************************************************************************** FAKEMEM READ/WRITE ***************************************************************************/ @@ -496,9 +523,23 @@ WRITE8_MEMBER( epson_lx810l_t::pf_stepper ) WRITE8_MEMBER( epson_lx810l_t::cr_stepper ) { + int m_cr_pos_abs_prev = m_cr_pos_abs; + stepper_update(1, data); m_cr_pos_abs = 200 - stepper_get_absolute_position(1); + if (m_cr_pos_abs > m_cr_pos_abs_prev) { + /* going right */ + m_real_cr_dir = 1; + } else { + /* going left */ + m_real_cr_dir = -1; + } + + if (!m_real_cr_steps) + timer_set(attotime::from_usec(400), TIMER_CR, m_real_cr_dir); + m_real_cr_steps++; + LX810LLOG("%s: %s(%02x); abs %d\n", machine().describe_context(), __func__, data, m_cr_pos_abs); } @@ -518,25 +559,18 @@ WRITE_LINE_MEMBER( epson_lx810l_t::co0_w ) /* Printhead is being fired on !state. */ if (!state) { - int pos = m_cr_pos_abs; - - /* HACK to get fire positions for motor in movement. The firmware - * issues two half-steps one immediately after the other. A timer - * fires the printhead twice. Supposedly, the first time the - * printhead is fired, it is midway between one step and the other. - * Ideally, the stepper motor interface should model the physics - * of the motors. For the moment, we adjust pos to get the - * intermediate position. + /* The firmware expects a 300 microseconds delay between the fire + * signal and the impact of the printhead on the paper. This can be + * verified by the timings of the steps and fire signals for the + * same positions with different directions (left to right or right + * to left). We don't simulate this delay since it is smaller than + * the time it takes the printhead to travel one pixel (which would + * be 417 microseconds), so it makes no difference to us. + * It is interesting to note that the vertical alignment between + * lines which are being printed in different directions is + * noticeably off in the 20+ years old printer used for testing =). */ - - if (m_cr_pos_abs > m_last_fire + 1) - pos--; - else if (m_cr_pos_abs < m_last_fire - 1) - pos++; - - LX810LLOG("FIRE0 %d %d %04x\n", m_pf_pos_abs, pos, m_printhead); - - m_last_fire = pos; + LX810LLOG("FIRE0 %d %d %04x\n", m_pf_pos_abs, m_real_cr_pos, m_printhead); } } diff --git a/src/emu/bus/centronics/epson_lx810l.h b/src/emu/bus/centronics/epson_lx810l.h index 8ff835facba..6ac3446f59b 100644 --- a/src/emu/bus/centronics/epson_lx810l.h +++ b/src/emu/bus/centronics/epson_lx810l.h @@ -96,6 +96,7 @@ protected: // device-level overrides virtual void device_start(); virtual void device_reset(); + virtual void device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr); private: required_device m_maincpu; @@ -108,8 +109,14 @@ private: UINT16 m_printhead; int m_pf_pos_abs; int m_cr_pos_abs; - int m_last_fire; /* HACK to get fire positions for motor in movement */ + int m_real_cr_pos; + int m_real_cr_steps; + int m_real_cr_dir; /* 1 is going right, -1 is going left */ UINT8 m_fakemem; + + enum { + TIMER_CR, + }; }; // ======================> epson_ap2000_t