1 // license:BSD-3-Clause
2 // copyright-holders:Ramiro Polla, Felipe Sanches
3 /*
4  * Epson LX-810L dot matrix printer emulation
5  *
6  * IC list:
7  *   uPD7810HG (cpu)
8  *   E05A30 (gate array)
9  *   2064C (8k RAM)
10  *   ER59256 (EEP-ROM - serial nvram)
11  *   SLA7020M (step motor driver)
12  *   uPC494C (pulse width modulation control)
13  *
14  * Devices boot and enter main input loop. Data is received through the
15  * centronics bus and printed as expected in a separate screen.
16  *
17  * It is possible to run the printers' self test with this procedure:
18  * - Turn on device;
19  * - Toggle Line Feed button (press 'L');
20  * - Reset device;
21  * - Toggle Line Feed button again;
22  * - Press Online button (press 'O');
23  *
24  * The printer's carriage will seek home and it will start printing
25  * some test data. The Online LED will blink at each line.
26  */
27 
28 #include "emu.h"
29 #include "epson_lx810l.h"
30 #include "speaker.h"
31 
32 //#define VERBOSE 1
33 #include "logmacro.h"
34 
35 //extern const char layout_lx800[]; /* use layout from lx800 */
36 
37 
38 /* The printer starts printing at x offset 44 and stops printing at x
39  * offset 1009, giving a total of 965 printable pixels. Supposedly, the
40  * border at the far right would be at x offset 1053. I've chosen the
41  * width for the paper as 1024, since it's a nicer number than 1053, so
42  * an offset must be used to centralize the pixels.
43  */
44 #define CR_OFFSET    (-14)
45 #define PAPER_WIDTH  1024
46 #define PAPER_HEIGHT 576
47 
48 
49 //**************************************************************************
50 //  DEVICE DEFINITIONS
51 //**************************************************************************
52 
53 DEFINE_DEVICE_TYPE(EPSON_LX810L, epson_lx810l_device, "lx810l", "Espon LX-810L")
54 DEFINE_DEVICE_TYPE(EPSON_AP2000, epson_ap2000_device, "ap2000", "Epson ActionPrinter 2000")
55 
56 
57 //-------------------------------------------------
58 //  ROM( lx810l )
59 //-------------------------------------------------
60 
ROM_START(lx810l)61 ROM_START( lx810l )
62 	ROM_REGION(0x8000, "maincpu", 0)
63 	ROM_LOAD("lx810l.ic3c", 0x0000, 0x8000, CRC(a66454e1) SHA1(8e6f2f98abcbd8af6e34b9ba746edf0d18aef843) )
64 	ROM_REGION16_BE(0x20, "eeprom", 0)
65 	ROM_LOAD( "at93c06", 0x00, 0x20, NO_DUMP )
66 ROM_END
67 
68 
69 //-------------------------------------------------
70 //  ROM( ap2000 )
71 //-------------------------------------------------
72 
73 ROM_START( ap2000 )
74 	ROM_REGION(0x8000, "maincpu", 0)
75 	ROM_LOAD("ap2k.ic3c", 0x0000, 0x8000, CRC(ee7294b7) SHA1(219ffa6ff661ce95d5772c9fc1967093718f04e9) )
76 	ROM_REGION16_BE(0x20, "eeprom", 0)
77 	ROM_LOAD( "at93c06", 0x00, 0x20, NO_DUMP )
78 ROM_END
79 
80 
81 //-------------------------------------------------
82 //  rom_region - device-specific ROM region
83 //-------------------------------------------------
84 
85 const tiny_rom_entry *epson_lx810l_device::device_rom_region() const
86 {
87 	return ROM_NAME( lx810l );
88 }
89 
90 
91 //-------------------------------------------------
92 //  rom_region - device-specific ROM region
93 //-------------------------------------------------
94 
device_rom_region() const95 const tiny_rom_entry *epson_ap2000_device::device_rom_region() const
96 {
97 	return ROM_NAME( ap2000 );
98 }
99 
100 
101 //-------------------------------------------------
102 //  ADDRESS_MAP( lx810l_mem )
103 //-------------------------------------------------
104 
lx810l_mem(address_map & map)105 void epson_lx810l_device::lx810l_mem(address_map &map)
106 {
107 	map(0x0000, 0x7fff).rom(); /* 32k firmware */
108 	map(0x8000, 0x9fff).ram(); /* 8k external RAM */
109 	map(0xa000, 0xbfff).rw(FUNC(epson_lx810l_device::fakemem_r), FUNC(epson_lx810l_device::fakemem_w)); /* fake memory, write one, set all */
110 	map(0xc000, 0xc00f).mirror(0x1ff0).rw("e05a30", FUNC(e05a30_device::read), FUNC(e05a30_device::write));
111 	map(0xe000, 0xfeff).noprw(); /* not used */
112 }
113 
114 
115 //-------------------------------------------------
116 //  device_add_mconfig - add device configuration
117 //-------------------------------------------------
118 
device_add_mconfig(machine_config & config)119 void epson_lx810l_device::device_add_mconfig(machine_config &config)
120 {
121 	/* basic machine hardware */
122 	upd7810_device &upd(UPD7810(config, m_maincpu, 14.7456_MHz_XTAL));
123 	upd.set_addrmap(AS_PROGRAM, &epson_lx810l_device::lx810l_mem);
124 	upd.pa_in_cb().set(FUNC(epson_lx810l_device::porta_r));
125 	upd.pa_out_cb().set(FUNC(epson_lx810l_device::porta_w));
126 	upd.pb_in_cb().set(FUNC(epson_lx810l_device::portb_r));
127 	upd.pb_out_cb().set(FUNC(epson_lx810l_device::portb_w));
128 	upd.pc_in_cb().set(FUNC(epson_lx810l_device::portc_r));
129 	upd.pc_out_cb().set(FUNC(epson_lx810l_device::portc_w));
130 	upd.an0_func().set(FUNC(epson_lx810l_device::an0_r));
131 	upd.an1_func().set(FUNC(epson_lx810l_device::an1_r));
132 	upd.an2_func().set(FUNC(epson_lx810l_device::an2_r));
133 	upd.an3_func().set(FUNC(epson_lx810l_device::an3_r));
134 	upd.an4_func().set(FUNC(epson_lx810l_device::an4_r));
135 	upd.an5_func().set(FUNC(epson_lx810l_device::an5_r));
136 	upd.an6_func().set(FUNC(epson_lx810l_device::an6_r));
137 	upd.an7_func().set(FUNC(epson_lx810l_device::an7_r));
138 	upd.co0_func().set(FUNC(epson_lx810l_device::co0_w));
139 	upd.co1_func().set("dac", FUNC(dac_bit_interface::write));
140 
141 //  config.set_default_layout(layout_lx800);
142 
143 	/* video hardware (simulates paper) */
144 	screen_device &screen(SCREEN(config, m_screen, SCREEN_TYPE_RASTER));
145 	screen.set_refresh_hz(60);
146 	screen.set_vblank_time(ATTOSECONDS_IN_USEC(0));
147 	screen.set_size(PAPER_WIDTH, PAPER_HEIGHT);
148 	screen.set_visarea(0, PAPER_WIDTH-1, 0, PAPER_HEIGHT-1);
149 	screen.set_screen_update(FUNC(epson_lx810l_device::screen_update_lx810l));
150 
151 	/* audio hardware */
152 	SPEAKER(config, "speaker").front_center();
153 	DAC_1BIT(config, "dac", 0).add_route(ALL_OUTPUTS, "speaker", 0.25);
154 
155 	/* gate array */
156 	e05a30_device &e05a30(E05A30(config, m_e05a30, 0));
157 	e05a30.printhead().set(FUNC(epson_lx810l_device::printhead));
158 	e05a30.pf_stepper().set(FUNC(epson_lx810l_device::pf_stepper));
159 	e05a30.cr_stepper().set(FUNC(epson_lx810l_device::cr_stepper));
160 	e05a30.ready().set(FUNC(epson_lx810l_device::e05a30_ready));
161 	e05a30.centronics_ack().set(FUNC(epson_lx810l_device::e05a30_centronics_ack));
162 	e05a30.centronics_busy().set(FUNC(epson_lx810l_device::e05a30_centronics_busy));
163 	e05a30.centronics_perror().set(FUNC(epson_lx810l_device::e05a30_centronics_perror));
164 	e05a30.centronics_fault().set(FUNC(epson_lx810l_device::e05a30_centronics_fault));
165 	e05a30.centronics_select().set(FUNC(epson_lx810l_device::e05a30_centronics_select));
166 
167 	/* 256-bit eeprom */
168 	EEPROM_93C06_16BIT(config, "eeprom");
169 
170 	STEPPER(config, m_pf_stepper, (uint8_t)4);
171 	STEPPER(config, m_cr_stepper, (uint8_t)2);
172 }
173 
174 
175 /***************************************************************************
176     INPUT PORTS
177 ***************************************************************************/
178 
179 static INPUT_PORTS_START( epson_lx810l )
180 
181 	/* Buttons on printer */
182 	PORT_START("ONLINE")
PORT_CODE(KEYCODE_0_PAD)183 	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("On Line") PORT_CODE(KEYCODE_0_PAD) PORT_CHANGED_MEMBER(DEVICE_SELF, epson_lx810l_device, online_sw, 0)
184 	PORT_START("FORMFEED")
185 	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Form Feed") PORT_CODE(KEYCODE_7_PAD)
186 	PORT_START("LINEFEED")
187 	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Line Feed") PORT_CODE(KEYCODE_9_PAD)
188 	PORT_START("LOADEJECT")
189 	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Load/Eject") PORT_CODE(KEYCODE_1_PAD)
190 	PORT_START("PAPEREND")
191 	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Paper End Sensor") PORT_CODE(KEYCODE_6_PAD)
192 
193 	/* DIPSW1 */
194 	PORT_START("DIPSW1")
195 
196 	PORT_DIPNAME(0x01, 0x01, "Character spacing")
197 	PORT_DIPLOCATION("DIP:1")
198 	PORT_DIPSETTING(0x01, "12 cpi") /* default */
199 	PORT_DIPSETTING(0x00, "10 cpi")
200 
201 	PORT_DIPNAME(0x02, 0x00, "Shape of zero")
202 	PORT_DIPLOCATION("DIP:2")
203 	PORT_DIPSETTING(0x02, "Slashed")
204 	PORT_DIPSETTING(0x00, "Not slashed") /* default */
205 
206 	PORT_DIPNAME(0x0c, 0x08, "Page length")
207 	PORT_DIPLOCATION("DIP:3,4")
208 	PORT_DIPSETTING(0x00, "11 inches")
209 	PORT_DIPSETTING(0x04, "12 inches")
210 	PORT_DIPSETTING(0x08, "8.5 inches") /* default */
211 	PORT_DIPSETTING(0x0c, "11.7 inches")
212 
213 	PORT_DIPNAME(0x10, 0x10, "Character table")
214 	PORT_DIPLOCATION("DIP:5")
215 	PORT_DIPSETTING(0x10, "Graphics") /* default */
216 	PORT_DIPSETTING(0x00, "Italics")
217 
218 	PORT_DIPNAME(0xe0, 0xe0, "International characters and PC selection")
219 	PORT_DIPLOCATION("DIP:6,7,8")
220 	PORT_DIPSETTING(0xe0, "United States") /* default */
221 	PORT_DIPSETTING(0x60, "France")
222 	PORT_DIPSETTING(0xa0, "Germany")
223 	PORT_DIPSETTING(0x20, "United Kingdom")
224 	PORT_DIPSETTING(0xc0, "Denmark")
225 	PORT_DIPSETTING(0x40, "Sweden")
226 	PORT_DIPSETTING(0x80, "Italy")
227 	PORT_DIPSETTING(0x00, "Spain")
228 
229 	/* DIPSW2 */
230 	PORT_START("DIPSW2")
231 
232 	PORT_DIPNAME(0x01, 0x01, "Short tear-off")
233 	PORT_DIPLOCATION("DIP:1")
234 	PORT_DIPSETTING(0x01, "Invalid") /* default */
235 	PORT_DIPSETTING(0x00, "Valid")
236 
237 	PORT_DIPNAME(0x02, 0x00, "Cut-sheet feeder mode")
238 	PORT_DIPLOCATION("DIP:2")
239 	PORT_DIPSETTING(0x02, "ON")
240 	PORT_DIPSETTING(0x00, "OFF") /* default */
241 
242 	PORT_DIPNAME(0x04, 0x00, "Skip-over-perforation")
243 	PORT_DIPLOCATION("DIP:3")
244 	PORT_DIPSETTING(0x04, "ON")
245 	PORT_DIPSETTING(0x00, "OFF") /* default */
246 
247 	PORT_DIPNAME(0x08, 0x00, "Auto line feed")
248 	PORT_DIPLOCATION("DIP:4")
249 	PORT_DIPSETTING(0x08, "ON")
250 	PORT_DIPSETTING(0x00, "OFF") /* default */
251 
252 INPUT_PORTS_END
253 
254 
255 //-------------------------------------------------
256 //  input_ports - device-specific input ports
257 //-------------------------------------------------
258 
259 ioport_constructor epson_lx810l_device::device_input_ports() const
260 {
261 	return INPUT_PORTS_NAME( epson_lx810l );
262 }
263 
INPUT_CHANGED_MEMBER(epson_lx810l_device::online_sw)264 INPUT_CHANGED_MEMBER(epson_lx810l_device::online_sw)
265 {
266 	m_maincpu->set_input_line(UPD7810_INTF2, newval ? CLEAR_LINE : ASSERT_LINE);
267 }
268 
269 
270 //**************************************************************************
271 //  LIVE DEVICE
272 //**************************************************************************
273 
274 //-------------------------------------------------
275 //  epson_lx810l_device - constructor
276 //-------------------------------------------------
277 
epson_lx810l_device(const machine_config & mconfig,const char * tag,device_t * owner,uint32_t clock)278 epson_lx810l_device::epson_lx810l_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) :
279 	epson_lx810l_device(mconfig, EPSON_LX810L, tag, owner, clock)
280 {
281 }
282 
epson_lx810l_device(const machine_config & mconfig,device_type type,const char * tag,device_t * owner,uint32_t clock)283 epson_lx810l_device::epson_lx810l_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock) :
284 	device_t(mconfig, type, tag, owner, clock),
285 	device_centronics_peripheral_interface(mconfig, *this),
286 	m_maincpu(*this, "maincpu"),
287 	m_pf_stepper(*this, "pf_stepper"),
288 	m_cr_stepper(*this, "cr_stepper"),
289 	m_eeprom(*this, "eeprom"),
290 	m_e05a30(*this, "e05a30"),
291 	m_screen(*this, "screen"),
292 	m_online_led(*this, "online_led"),
293 	m_93c06_clk(0),
294 	m_93c06_cs(0),
295 	m_printhead(0),
296 	m_pf_pos_abs(1),
297 	m_cr_pos_abs(1),
298 	m_real_cr_pos(1),
299 	m_real_cr_steps(0),
300 	m_real_cr_dir(0), m_fakemem(0)
301 {
302 }
303 
epson_ap2000_device(const machine_config & mconfig,const char * tag,device_t * owner,uint32_t clock)304 epson_ap2000_device::epson_ap2000_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
305 	: epson_lx810l_device(mconfig, EPSON_AP2000, tag, owner, clock)
306 { }
307 
308 
309 //-------------------------------------------------
310 //  device_start - device-specific startup
311 //-------------------------------------------------
312 
device_start()313 void epson_lx810l_device::device_start()
314 {
315 	m_online_led.resolve();
316 
317 	m_cr_timer = timer_alloc(TIMER_CR);
318 
319 	m_screen->register_screen_bitmap(m_bitmap);
320 	m_bitmap.fill(0xffffff); /* Start with a clean white piece of paper */
321 }
322 
323 
324 //-------------------------------------------------
325 //  device_reset - device-specific reset
326 //-------------------------------------------------
327 
device_reset()328 void epson_lx810l_device::device_reset()
329 {
330 }
331 
332 
333 //-------------------------------------------------
334 //  device_timer - device-specific timer
335 //-------------------------------------------------
336 
device_timer(emu_timer & timer,device_timer_id id,int param,void * ptr)337 void epson_lx810l_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr)
338 {
339 	switch (id) {
340 	case TIMER_CR:
341 		/* The firmware issues two half-steps in sequence, one immediately
342 		 * after the other. At full speed, the motor does two half-steps at
343 		 * each 833 microseconds. A timer fires the printhead twice, with
344 		 * the same period as each half-step (417 microseconds), but with
345 		 * a 356 microseconds delay relative to the motor steps.
346 		 */
347 		m_real_cr_pos += param;
348 		m_real_cr_steps--;
349 		if (m_real_cr_steps)
350 			m_cr_timer->adjust(attotime::from_usec(400), m_real_cr_dir);
351 		break;
352 	}
353 }
354 
355 
356 /***************************************************************************
357     FAKEMEM READ/WRITE
358 ***************************************************************************/
359 
fakemem_r()360 uint8_t epson_lx810l_device::fakemem_r()
361 {
362 	return m_fakemem;
363 }
364 
fakemem_w(uint8_t data)365 void epson_lx810l_device::fakemem_w(uint8_t data)
366 {
367 	m_fakemem = data;
368 }
369 
370 
371 /***************************************************************************
372     I/O PORTS
373 ***************************************************************************/
374 
375 /*
376  * PA0  R   CN7 sensor (Home Position, HP, active low)
377  * PA1  R   CN6 sensor (Paper-End, PE, active low)
378  * PA2  R   CN4 sensor (Release, low = tractor)
379  * PA3   W  Stepper motor voltage reference (these 3 pins make up one voltage)
380  * PA4   W  Stepper motor voltage reference (these 3 pins make up one voltage)
381  * PA5   W  Stepper motor voltage reference (these 3 pins make up one voltage)
382  * PA6  R   Line Feed SWITCH
383  * PA7  R   Form Feed SWITCH
384  */
porta_r(offs_t offset)385 uint8_t epson_lx810l_device::porta_r(offs_t offset)
386 {
387 	uint8_t result = 0;
388 	uint8_t hp_sensor = m_cr_pos_abs <= 0 ? 0 : 1;
389 	//uint8_t pe_sensor = m_pf_pos_abs <= 0 ? 1 : 0;
390 
391 	result |= hp_sensor; /* home position */
392 	//result |= pe_sensor << 1; /* paper end */
393 	result |= ioport("PAPEREND")->read() << 1;  // simulate a paper out error
394 	result |= ioport("LINEFEED")->read() << 6;
395 	result |= ioport("FORMFEED")->read() << 7;
396 
397 	LOG("%s: lx810l_PA_r(%02x): result %02x\n", machine().describe_context(), offset, result);
398 
399 	return result;
400 }
401 
porta_w(offs_t offset,uint8_t data)402 void epson_lx810l_device::porta_w(offs_t offset, uint8_t data)
403 {
404 	LOG("%s: lx810l_PA_w(%02x): %02x: stepper vref %d\n", machine().describe_context(), offset, data, BIT(data, 3) | (BIT(data, 4)<<1) | (BIT(data, 5)<<2));
405 }
406 
407 /*
408  * PB0  R   DIP1.0 & 93C06.DO
409  * PB1  RW  DIP1.1 & 93C06.DI
410  * PB2  R   DIP1.2
411  * PB3  R   DIP1.3
412  * PB4  R   DIP1.4
413  * PB5  R   DIP1.5
414  * PB6  R   DIP1.6
415  * PB7  R   DIP1.7
416  */
portb_r(offs_t offset)417 uint8_t epson_lx810l_device::portb_r(offs_t offset)
418 {
419 	uint8_t result = ~ioport("DIPSW1")->read();
420 
421 	/* if 93C06 is selected */
422 	if (m_93c06_cs) {
423 		uint8_t do_r = m_eeprom->do_read();
424 		result &= 0xfe;
425 		result |= do_r;
426 	}
427 
428 	LOG("%s: lx810l_PB_r(%02x): result %02x\n", machine().describe_context(), offset, result);
429 
430 	return result;
431 }
432 
portb_w(offs_t offset,uint8_t data)433 void epson_lx810l_device::portb_w(offs_t offset, uint8_t data)
434 {
435 	uint8_t data_in = BIT(data, 1);
436 
437 	/* if 93C06 is selected */
438 	if (m_93c06_cs)
439 		m_eeprom->di_write(data_in);
440 
441 	LOG("%s: lx810l_PB_w(%02x): %02x: 93c06 data %d\n", machine().describe_context(), offset, data, data_in);
442 }
443 
444 /*
445  * PC0   W  TXD        serial i/o txd, also TAMA.25
446  * PC1  R   RXD        serial i/o rxd, also E05A30.28
447  * PC2   W  ONLINE LP  online led
448  * PC3  R   ONLINE SW  online switch
449  * PC4   W  93C06.SK
450  * PC5   W  93C06.CS
451  * PC6   W  FIRE       drive pulse width signal, also E05A30.57
452  * PC7   W  BUZZER     buzzer signal
453  */
portc_r(offs_t offset)454 uint8_t epson_lx810l_device::portc_r(offs_t offset)
455 {
456 	uint8_t result = 0;
457 
458 	/* result |= ioport("serial")->read() << 1; */
459 	result |= !ioport("ONLINE")->read() << 3;
460 	result |= m_93c06_clk << 4;
461 	result |= m_93c06_cs  << 5;
462 
463 	LOG("%s: lx810l_PC_r(%02x): %02x\n", machine().describe_context(), offset, result);
464 
465 	return result;
466 }
467 
portc_w(offs_t offset,uint8_t data)468 void epson_lx810l_device::portc_w(offs_t offset, uint8_t data)
469 {
470 	/* ioport("serial")->write(BIT(data, 0)); */
471 
472 	m_93c06_clk =  BIT(data, 4);
473 	m_93c06_cs  = !BIT(data, 5);
474 
475 	LOG("%s: PC_w(%02x): %02x 93c06 clk: %d cs: %d\n", machine().describe_context(), offset, data, m_93c06_clk, m_93c06_cs);
476 
477 	m_eeprom->clk_write(m_93c06_clk ? ASSERT_LINE : CLEAR_LINE);
478 	m_eeprom->cs_write (m_93c06_cs  ? ASSERT_LINE : CLEAR_LINE);
479 
480 	m_online_led = !BIT(data, 2);
481 }
482 
483 
484 /***************************************************************************
485     GATE ARRAY
486 ***************************************************************************/
487 
printhead(uint16_t data)488 void epson_lx810l_device::printhead(uint16_t data)
489 {
490 	m_printhead = data;
491 }
492 
pf_stepper(uint8_t data)493 void epson_lx810l_device::pf_stepper(uint8_t data)
494 {
495 	int changed = m_pf_stepper->update(data);
496 	m_pf_pos_abs = -m_pf_stepper->get_absolute_position();
497 
498 	/* clear last line of paper */
499 	if (changed > 0) {
500 		void *line = m_bitmap.raw_pixptr(bitmap_line(9), 0);
501 		memset(line, 0xff, m_bitmap.width() * 4);
502 	}
503 
504 	LOG("%s: %s(%02x); abs %d\n", machine().describe_context(), __func__, data, m_pf_pos_abs);
505 }
506 
cr_stepper(uint8_t data)507 void epson_lx810l_device::cr_stepper(uint8_t data)
508 {
509 	int m_cr_pos_abs_prev = m_cr_pos_abs;
510 
511 	m_cr_stepper->update(data);
512 	m_cr_pos_abs = -m_cr_stepper->get_absolute_position();
513 
514 	if (m_cr_pos_abs > m_cr_pos_abs_prev) {
515 		/* going right */
516 		m_real_cr_dir =  1;
517 	} else {
518 		/* going left */
519 		m_real_cr_dir = -1;
520 	}
521 
522 	if (!m_real_cr_steps)
523 		m_cr_timer->adjust(attotime::from_usec(400), m_real_cr_dir);
524 	m_real_cr_steps++;
525 
526 	LOG("%s: %s(%02x); abs %d\n", machine().describe_context(), __func__, data, m_cr_pos_abs);
527 }
528 
WRITE_LINE_MEMBER(epson_lx810l_device::e05a30_ready)529 WRITE_LINE_MEMBER( epson_lx810l_device::e05a30_ready )
530 {
531 	// must be longer than attotime::zero - 0.09 is minimum to initialize properly
532 	m_maincpu->pulse_input_line(INPUT_LINE_NMI, attotime::from_double(0.09));
533 }
534 
535 
536 /***************************************************************************
537     Video hardware (simulates paper)
538 ***************************************************************************/
539 
screen_update_lx810l(screen_device & screen,bitmap_rgb32 & bitmap,const rectangle & cliprect)540 uint32_t epson_lx810l_device::screen_update_lx810l(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
541 {
542 	int scrolly = -bitmap_line(9);
543 	copyscrollbitmap(bitmap, m_bitmap, 0, nullptr, 1, &scrolly, cliprect);
544 
545 	/* draw "printhead" */
546 	int bordersize = 1;
547 	bitmap.plot_box(m_real_cr_pos + CR_OFFSET - 10 - bordersize, PAPER_HEIGHT - 36 - bordersize, 20 + bordersize * 2, 36 + bordersize * 2, 0x000000 );
548 	bitmap.plot_box(m_real_cr_pos + CR_OFFSET - 10, PAPER_HEIGHT - 36, 20, 36, m_e05a30->ready_led() ? 0x55ff55 : 0x337733 );
549 
550 	return 0;
551 }
552 
553 
554 /***************************************************************************
555     Extended Timer Output
556 ***************************************************************************/
557 
WRITE_LINE_MEMBER(epson_lx810l_device::co0_w)558 WRITE_LINE_MEMBER( epson_lx810l_device::co0_w )
559 {
560 	/* Printhead is being fired on !state. */
561 	if (!state) {
562 		/* The firmware expects a 300 microseconds delay between the fire
563 		 * signal and the impact of the printhead on the paper. This can be
564 		 * verified by the timings of the steps and fire signals for the
565 		 * same positions with different directions (left to right or right
566 		 * to left). We don't simulate this delay since it is smaller than
567 		 * the time it takes the printhead to travel one pixel (which would
568 		 * be 417 microseconds), so it makes no difference to us.
569 		 * It is interesting to note that the vertical alignment between
570 		 * lines which are being printed in different directions is
571 		 * noticeably off in the 20+ years old printer used for testing =).
572 		 */
573 		if (m_real_cr_pos < m_bitmap.width()) {
574 			for (int i = 0; i < 9; i++) {
575 				unsigned int const y = bitmap_line(i);
576 				if ((m_printhead & (1<<(8-i))) != 0)
577 					m_bitmap.pix(y, m_real_cr_pos + CR_OFFSET) = 0x000000;
578 			}
579 		}
580 	}
581 }
582 
583 
584 /***************************************************************************
585     ADC
586 ***************************************************************************/
587 
an0_r()588 uint8_t epson_lx810l_device::an0_r()
589 {
590 	uint8_t res = !!(ioport("DIPSW2")->read() & 0x01);
591 	return res - 1; /* DIPSW2.1 */
592 }
593 
an1_r()594 uint8_t epson_lx810l_device::an1_r()
595 {
596 	uint8_t res = !!(ioport("DIPSW2")->read() & 0x02);
597 	return res - 1; /* DIPSW2.2 */
598 }
599 
an2_r()600 uint8_t epson_lx810l_device::an2_r()
601 {
602 	uint8_t res = !!(ioport("DIPSW2")->read() & 0x04);
603 	return res - 1; /* DIPSW2.3 */
604 }
605 
an3_r()606 uint8_t epson_lx810l_device::an3_r()
607 {
608 	uint8_t res = !!(ioport("DIPSW2")->read() & 0x08);
609 	return res - 1; /* DIPSW2.4 */
610 }
611 
an4_r()612 uint8_t epson_lx810l_device::an4_r()
613 {
614 	return 0xff;
615 }
616 
an5_r()617 uint8_t epson_lx810l_device::an5_r()
618 {
619 	return 0xCB; /* motor voltage, 0xcb = 24V */
620 }
621 
an6_r()622 uint8_t epson_lx810l_device::an6_r()
623 {
624 	uint8_t res = !ioport("LOADEJECT")->read();
625 	return res - 1;
626 }
627 
an7_r()628 uint8_t epson_lx810l_device::an7_r()
629 {
630 	return 0xff;
631 }
632