1 // license:BSD-3-Clause
2 // copyright-holders:Aaron Giles
3 /*************************************************************************
4 
5     ldv1000.c
6 
7     Pioneer LD-V1000 laserdisc emulation.
8 
9 **************************************************************************
10 
11     Still to do:
12 
13         * fix issues
14         * add OSD
15 
16 *************************************************************************/
17 
18 
19 #include "emu.h"
20 #include "ldv1000.h"
21 #include "machine/i8255.h"
22 #include "machine/z80ctc.h"
23 #include "cpu/z80/z80.h"
24 #include "machine/z80daisy.h"
25 
26 
27 
28 //**************************************************************************
29 //  DEBUGGING
30 //**************************************************************************
31 
32 #define LOG_PORT_IO                 0
33 #define LOG_STATUS_CHANGES          0
34 #define LOG_FRAMES_SEEN             0
35 #define LOG_COMMANDS                0
36 
37 
38 
39 //**************************************************************************
40 //  CONSTANTS
41 //**************************************************************************
42 
43 #define SCAN_SPEED                      (2000 / 30)         // 2000 frames/second
44 #define SEEK_FAST_SPEED                 (4000 / 30)         // 4000 frames/second
45 
46 #define MULTIJUMP_TRACK_TIME            attotime::from_usec(50)
47 
48 
49 
50 //**************************************************************************
51 //  GLOBAL VARIABLES
52 //**************************************************************************
53 
54 // devices
55 DEFINE_DEVICE_TYPE(PIONEER_LDV1000, pioneer_ldv1000_device, "ldv1000", "Pioneer LD-V1000")
56 
57 
58 
59 //**************************************************************************
60 //  LD-V1000 ROM AND MACHINE INTERFACES
61 //**************************************************************************
62 
ldv1000_map(address_map & map)63 void pioneer_ldv1000_device::ldv1000_map(address_map &map)
64 {
65 	map(0x0000, 0x1fff).mirror(0x6000).rom();
66 	map(0x8000, 0x87ff).mirror(0x3800).ram();
67 	map(0xc000, 0xc003).mirror(0x1ff0).rw("ldvppi0", FUNC(i8255_device::read), FUNC(i8255_device::write));
68 	map(0xc004, 0xc007).mirror(0x1ff0).rw("ldvppi1", FUNC(i8255_device::read), FUNC(i8255_device::write));
69 }
70 
71 
ldv1000_portmap(address_map & map)72 void pioneer_ldv1000_device::ldv1000_portmap(address_map &map)
73 {
74 	map.global_mask(0xff);
75 	map(0x00, 0x07).mirror(0x38).rw(FUNC(pioneer_ldv1000_device::z80_decoder_display_port_r), FUNC(pioneer_ldv1000_device::z80_decoder_display_port_w));
76 	map(0x40, 0x40).mirror(0x3f).r(FUNC(pioneer_ldv1000_device::z80_controller_r));
77 	map(0x80, 0x80).mirror(0x3f).w(FUNC(pioneer_ldv1000_device::z80_controller_w));
78 	map(0xc0, 0xc3).mirror(0x3c).rw(m_z80_ctc, FUNC(z80ctc_device::read), FUNC(z80ctc_device::write));
79 }
80 
81 
82 static const z80_daisy_config daisy_chain[] =
83 {
84 	{ "ldvctc" },
85 	{ nullptr }
86 };
87 
88 
89 ROM_START( ldv1000 )
90 	ROM_REGION( 0x2000, "ldv1000", 0 )
91 	ROM_LOAD( "z03_1001_vyw-053_v1-0.bin", 0x0000, 0x2000, CRC(31ec4687) SHA1(52f91c304a878ba02b2fa1cda1a9489d6dd5a34f) )
92 ROM_END
93 
94 
95 
96 //**************************************************************************
97 //  PIONEER LD-V1000 IMPLEMENTATION
98 //**************************************************************************
99 
100 //-------------------------------------------------
101 //  pioneer_ldv1000_device - constructor
102 //-------------------------------------------------
103 
pioneer_ldv1000_device(const machine_config & mconfig,const char * tag,device_t * owner,uint32_t clock)104 pioneer_ldv1000_device::pioneer_ldv1000_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
105 	: laserdisc_device(mconfig, PIONEER_LDV1000, tag, owner, clock),
106 		m_z80_cpu(*this, "ldv1000"),
107 		m_z80_ctc(*this, "ldvctc"),
108 		m_multitimer(nullptr),
109 		m_command_strobe_cb(*this),
110 		m_command(0),
111 		m_status(0),
112 		m_vsync(false),
113 		m_counter_start(0),
114 		m_counter(0),
115 		m_portc0(0),
116 		m_portb1(0),
117 		m_portc1(0),
118 		m_portselect(0),
119 		m_dispindex(0),
120 		m_vbiready(false),
121 		m_vbiindex(0)
122 {
123 }
124 
125 
126 //-------------------------------------------------
127 //  data_w - handle a parallel data write to the
128 //  LD-V1000
129 //-------------------------------------------------
130 
data_w(uint8_t data)131 void pioneer_ldv1000_device::data_w(uint8_t data)
132 {
133 	m_command = data;
134 	if (LOG_COMMANDS)
135 		logerror("-> COMMAND = %02X (%s)\n", data, (m_portc1 & 0x10) ? "valid" : "invalid");
136 }
137 
138 
139 //-------------------------------------------------
140 //  enter_w - set the state of the ENTER strobe
141 //-------------------------------------------------
142 
enter_w(uint8_t data)143 void pioneer_ldv1000_device::enter_w(uint8_t data)
144 {
145 }
146 
147 
148 //-------------------------------------------------
149 //  device_start - device initialization
150 //-------------------------------------------------
151 
device_start()152 void pioneer_ldv1000_device::device_start()
153 {
154 	// pass through to the parent
155 	laserdisc_device::device_start();
156 
157 	// allocate timers
158 	m_multitimer = timer_alloc(TID_MULTIJUMP);
159 
160 	m_command_strobe_cb.resolve_safe();
161 }
162 
163 
164 //-------------------------------------------------
165 //  device_reset - device reset
166 //-------------------------------------------------
167 
device_reset()168 void pioneer_ldv1000_device::device_reset()
169 {
170 	// pass through to the parent
171 	laserdisc_device::device_reset();
172 
173 	// reset our state
174 	m_command = 0;
175 	m_status = 0;
176 	m_vsync = false;
177 	m_counter_start = 0;
178 	m_counter = 0;
179 	m_portc0 = 0;
180 	m_portb1 = 0;
181 	m_portc1 = 0;
182 	m_portselect = 0;
183 	m_dispindex = 0;
184 	m_vbiready = false;
185 	m_vbiindex = 0;
186 }
187 
188 
189 //-------------------------------------------------
190 //  device_timer - handle timers set by this
191 //  device
192 //-------------------------------------------------
193 
device_timer(emu_timer & timer,device_timer_id id,int param,void * ptr)194 void pioneer_ldv1000_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr)
195 {
196 	switch (id)
197 	{
198 		case TID_MULTIJUMP:
199 		{
200 			// bit 5 of port B on PPI 1 selects the direction of slider movement
201 			int direction = (m_portb1 & 0x20) ? 1 : -1;
202 			advance_slider(direction);
203 
204 			// update down counter and reschedule
205 			if (--m_counter != 0)
206 				timer.adjust(MULTIJUMP_TRACK_TIME);
207 			break;
208 		}
209 
210 		case TID_VSYNC_OFF:
211 			m_vsync = false;
212 			break;
213 
214 		case TID_VBI_DATA_FETCH:
215 		{
216 			// appears to return data in reverse order
217 			uint32_t lines[3];
218 			lines[0] = get_field_code(LASERDISC_CODE_LINE1718, false);
219 			lines[1] = get_field_code(LASERDISC_CODE_LINE17, false);
220 			lines[2] = get_field_code(LASERDISC_CODE_LINE16, false);
221 
222 			// fill in the details
223 			memset(m_vbi, 0, sizeof(m_vbi));
224 			if (focus_on() && laser_on())
225 			{
226 				// loop over lines
227 				for (int line = 0; line < 3; line++)
228 				{
229 					uint8_t *dest = &m_vbi[line * 7];
230 					uint32_t data = lines[line];
231 
232 					// the logic only processes leadin/leadout/frame number codes
233 					if (data == VBI_CODE_LEADIN || data == VBI_CODE_LEADOUT || (data & VBI_MASK_CAV_PICTURE) == VBI_CODE_CAV_PICTURE)
234 					{
235 						*dest++ = 0x09 | (((data & VBI_MASK_CAV_PICTURE) == VBI_CODE_CAV_PICTURE) ? 0x02 : 0x00);
236 						*dest++ = 0x08;
237 						*dest++ = (data >> 16) & 0x0f;
238 						*dest++ = (data >> 12) & 0x0f;
239 						*dest++ = (data >>  8) & 0x0f;
240 						*dest++ = (data >>  4) & 0x0f;
241 						*dest++ = (data >>  0) & 0x0f;
242 					}
243 				}
244 			}
245 
246 			// signal that data is ready and reset the readback index
247 			m_vbiready = true;
248 			m_vbiindex = 0;
249 			break;
250 		}
251 
252 		// pass everything else onto the parent
253 		default:
254 			laserdisc_device::device_timer(timer, id, param, ptr);
255 			break;
256 	}
257 }
258 
259 
260 //-------------------------------------------------
261 //  device_rom_region - return a pointer to our
262 //  ROM region definitions
263 //-------------------------------------------------
264 
device_rom_region() const265 const tiny_rom_entry *pioneer_ldv1000_device::device_rom_region() const
266 {
267 	return ROM_NAME(ldv1000);
268 }
269 
270 
271 //-------------------------------------------------
272 //  device_add_mconfig - add device configuration
273 //-------------------------------------------------
274 
device_add_mconfig(machine_config & config)275 void pioneer_ldv1000_device::device_add_mconfig(machine_config &config)
276 {
277 	Z80(config, m_z80_cpu, XTAL(5'000'000)/2);
278 	m_z80_cpu->set_daisy_config(daisy_chain);
279 	m_z80_cpu->set_addrmap(AS_PROGRAM, &pioneer_ldv1000_device::ldv1000_map);
280 	m_z80_cpu->set_addrmap(AS_IO, &pioneer_ldv1000_device::ldv1000_portmap);
281 
282 	Z80CTC(config, m_z80_ctc, XTAL(5'000'000)/2);
283 	m_z80_ctc->intr_callback().set(FUNC(pioneer_ldv1000_device::ctc_interrupt));
284 
285 	i8255_device &ldvppi0(I8255(config, "ldvppi0"));
286 	ldvppi0.out_pa_callback().set(FUNC(pioneer_ldv1000_device::ppi0_porta_w));
287 	ldvppi0.in_pb_callback().set(FUNC(pioneer_ldv1000_device::ppi0_portb_r));
288 	ldvppi0.in_pc_callback().set(FUNC(pioneer_ldv1000_device::ppi0_portc_r));
289 	ldvppi0.out_pc_callback().set(FUNC(pioneer_ldv1000_device::ppi0_portc_w));
290 
291 	i8255_device &ldvppi1(I8255(config, "ldvppi1"));
292 	ldvppi1.in_pa_callback().set(FUNC(pioneer_ldv1000_device::ppi1_porta_r));
293 	ldvppi1.out_pb_callback().set(FUNC(pioneer_ldv1000_device::ppi1_portb_w));
294 	ldvppi1.out_pc_callback().set(FUNC(pioneer_ldv1000_device::ppi1_portc_w));
295 }
296 
297 
298 //-------------------------------------------------
299 //  player_vsync - VSYNC callback, called at the
300 //  start of the blanking period
301 //-------------------------------------------------
302 
player_vsync(const vbi_metadata & vbi,int fieldnum,const attotime & curtime)303 void pioneer_ldv1000_device::player_vsync(const vbi_metadata &vbi, int fieldnum, const attotime &curtime)
304 {
305 	// generate interrupts if we hit the edges
306 	slider_position sliderpos = get_slider_position();
307 	m_z80_ctc->trg1(sliderpos == SLIDER_MINIMUM);
308 	m_z80_ctc->trg2(sliderpos == SLIDER_MAXIMUM);
309 
310 	// signal VSYNC and set a timer to turn it off
311 	m_vsync = true;
312 	timer_set(screen().scan_period() * 4, TID_VSYNC_OFF);
313 
314 	// also set a timer to fetch the VBI data when it is ready
315 	timer_set(screen().time_until_pos(19*2), TID_VBI_DATA_FETCH);
316 
317 	// boost interleave for the first 1ms to improve communications
318 	machine().scheduler().boost_interleave(attotime::zero, attotime::from_msec(1));
319 }
320 
321 
322 //-------------------------------------------------
323 //  player_update - update callback, called on
324 //  the first visible line of the frame
325 //-------------------------------------------------
326 
player_update(const vbi_metadata & vbi,int fieldnum,const attotime & curtime)327 int32_t pioneer_ldv1000_device::player_update(const vbi_metadata &vbi, int fieldnum, const attotime &curtime)
328 {
329 	if (LOG_FRAMES_SEEN)
330 	{
331 		int frame = frame_from_metadata(vbi);
332 		if (frame != FRAME_NOT_PRESENT) logerror("== %d\n", frame);
333 	}
334 	return fieldnum;
335 }
336 
337 
338 //-------------------------------------------------
339 //  ctc_interrupt - called when the CTC triggers
340 //  an interrupt in the daisy chain
341 //-------------------------------------------------
342 
ctc_interrupt(int state)343 void pioneer_ldv1000_device::ctc_interrupt(int state)
344 {
345 	m_z80_cpu->set_input_line(0, state ? ASSERT_LINE : CLEAR_LINE);
346 }
347 
348 
349 //-------------------------------------------------
350 //  z80_decoder_display_port_w - handle writes to
351 //  the decoder/display chips
352 //-------------------------------------------------
353 
z80_decoder_display_port_w(offs_t offset,uint8_t data)354 void pioneer_ldv1000_device::z80_decoder_display_port_w(offs_t offset, uint8_t data)
355 {
356 	/*
357 	    TX/RX = /A0 (A0=0 -> TX, A0=1 -> RX)
358 
359 	    Display is 6-bit
360 	    Decoder is 4-bit
361 	*/
362 
363 	// writes to offset 0 select the target for reads/writes of actual data
364 	if (offset == 0)
365 	{
366 		m_portselect = data;
367 		m_dispindex = 0;
368 	}
369 
370 	// writes to offset 2 constitute actual writes targeted toward the display and decoder chips
371 	else if (offset == 2)
372 	{
373 		// selections 0 and 1 represent the two display lines; only 6 bits are transferred
374 		if (m_portselect < 2)
375 			m_display[m_portselect][m_dispindex++ % 20] = data & 0x3f;
376 	}
377 }
378 
379 
380 //-------------------------------------------------
381 //  z80_decoder_display_port_r - handle reads from the
382 //  decoder/display chips
383 //-------------------------------------------------
384 
z80_decoder_display_port_r(offs_t offset)385 uint8_t pioneer_ldv1000_device::z80_decoder_display_port_r(offs_t offset)
386 {
387 	// reads from offset 3 constitute actual reads from the display and decoder chips
388 	uint8_t result = 0;
389 	if (offset == 3)
390 	{
391 		// selection 4 represents the VBI data reading
392 		if (m_portselect == 4)
393 		{
394 			m_vbiready = false;
395 			result = m_vbi[m_vbiindex++ % ARRAY_LENGTH(m_vbi)];
396 		}
397 	}
398 	return result;
399 }
400 
401 
402 //-------------------------------------------------
403 //  z80_controller_r - handle read of the data from
404 //  the controlling system
405 //-------------------------------------------------
406 
z80_controller_r()407 uint8_t pioneer_ldv1000_device::z80_controller_r()
408 {
409 	// note that this is a cheesy implementation; the real thing relies on exquisite timing
410 	uint8_t result = m_command ^ 0xff;
411 	m_command = 0xff;
412 	return result;
413 }
414 
415 
416 //-------------------------------------------------
417 //  z80_controller_w - handle status latch writes
418 //-------------------------------------------------
419 
z80_controller_w(uint8_t data)420 void pioneer_ldv1000_device::z80_controller_w(uint8_t data)
421 {
422 	if (LOG_STATUS_CHANGES && data != m_status)
423 		logerror("%s:CONTROLLER.W=%02X\n", machine().describe_context(), data);
424 	m_status = data;
425 }
426 
427 
428 //-------------------------------------------------
429 //  ppi0_porta_w - handle writes to port A of
430 //  PPI #0
431 //-------------------------------------------------
432 
ppi0_porta_w(uint8_t data)433 void pioneer_ldv1000_device::ppi0_porta_w(uint8_t data)
434 {
435 	m_counter_start = data;
436 	if (LOG_PORT_IO)
437 		logerror("%s:PORTA.0=%02X\n", machine().describe_context(), data);
438 }
439 
440 
441 //-------------------------------------------------
442 //  ppi0_portb_r - handle reads from port B of
443 //  PPI #0
444 //-------------------------------------------------
445 
ppi0_portb_r()446 uint8_t pioneer_ldv1000_device::ppi0_portb_r()
447 {
448 	return m_counter;
449 }
450 
451 
452 //-------------------------------------------------
453 //  ppi0_portc_r - handle reads from port C of
454 //  PPI #0
455 //-------------------------------------------------
456 
ppi0_portc_r()457 uint8_t pioneer_ldv1000_device::ppi0_portc_r()
458 {
459 	/*
460 	    $10 = /VSYNC
461 	    $20 = IRQ from decoder chip
462 	    $40 = TRKG LOOP (N24-1)
463 	    $80 = DUMP (N20-1) -- code reads the state and waits for it to change
464 	*/
465 
466 	uint8_t result = 0x00;
467 	if (!m_vsync)
468 		result |= 0x10;
469 	if (!m_vbiready)
470 		result |= 0x20;
471 	return result;
472 }
473 
474 
475 //-------------------------------------------------
476 //  ppi0_portc_w - handle writes to port C of
477 //  PPI #0
478 //-------------------------------------------------
479 
ppi0_portc_w(uint8_t data)480 void pioneer_ldv1000_device::ppi0_portc_w(uint8_t data)
481 {
482 	/*
483 	    $01 = preload on up/down counters
484 	    $02 = /MULTI JUMP TRIG
485 	    $04 = SCAN MODE
486 	    $08 = n/c
487 	*/
488 
489 	// set the new value
490 	uint8_t prev = m_portc0;
491 	m_portc0 = data;
492 	if (LOG_PORT_IO && ((data ^ prev) & 0x0f) != 0)
493 	{
494 		logerror("%s:PORTC.0=%02X%s%s%s\n", machine().describe_context(), data,
495 			(data & 0x01) ? " PRELOAD" : "",
496 			!(data & 0x02) ? " /MULTIJUMP" : "",
497 			(data & 0x04) ? " SCANMODE" : "");
498 	}
499 
500 	// on the rising edge of bit 0, clock the down counter load
501 	if ((data & 0x01) && !(prev & 0x01))
502 		m_counter = m_counter_start;
503 
504 	// on the falling edge of bit 1, start the multi-jump timer
505 	if (!(data & 0x02) && (prev & 0x02))
506 		m_multitimer->adjust(MULTIJUMP_TRACK_TIME);
507 }
508 
509 
510 //-------------------------------------------------
511 //  ppi1_porta_r - handle reads from port A of
512 //  PPI #1
513 //-------------------------------------------------
514 
ppi1_porta_r()515 uint8_t pioneer_ldv1000_device::ppi1_porta_r()
516 {
517 	/*
518 	    $01 = /FOCS LOCK
519 	    $02 = /SPDL LOCK
520 	    $04 = INSIDE
521 	    $08 = OUTSIDE
522 	    $10 = MOTOR STOP
523 	    $20 = +5V/test point
524 	    $40 = /INT LOCK
525 	    $80 = 8 INCH CHK
526 	*/
527 
528 	slider_position sliderpos = get_slider_position();
529 	uint8_t result = 0x00;
530 
531 	// bit 0: /FOCUS LOCK
532 	if (!focus_on())
533 		result |= 0x01;
534 
535 	// bit 1: /SPDL LOCK
536 	if (!spdl_on())
537 		result |= 0x02;
538 
539 	// bit 2: INSIDE signal
540 	if (sliderpos == SLIDER_MINIMUM)
541 		result |= 0x04;
542 
543 	// bit 3: OUTSIDE signal
544 	if (sliderpos == SLIDER_MAXIMUM)
545 		result |= 0x08;
546 
547 	// bit 4: MOTOR STOP
548 
549 	// bit 5: +5V/test point
550 	result |= 0x20;
551 
552 	// bit 6: /INT LOCK
553 
554 	// bit 7: 8 INCH CHK
555 
556 	return result;
557 }
558 
559 
560 //-------------------------------------------------
561 //  ppi1_portb_w - handle writes to port B of
562 //  PPI #1
563 //-------------------------------------------------
564 
ppi1_portb_w(uint8_t data)565 void pioneer_ldv1000_device::ppi1_portb_w(uint8_t data)
566 {
567 	/*
568 	    $01 = /FOCS ON
569 	    $02 = /SPDL RUN
570 	    $04 = /JUMP TRIG
571 	    $08 = /SCAN A
572 	    $10 = SCAN B
573 	    $20 = SCAN C
574 	    $40 = /LASER ON
575 	    $80 = /SYNC ST0
576 	*/
577 
578 	// set the new value
579 	uint8_t prev = m_portb1;
580 	m_portb1 = data;
581 	if (LOG_PORT_IO && ((data ^ prev) & 0xff) != 0)
582 	{
583 		logerror("%s:PORTB.1=%02X: %s%s%s%s%s%s\n", machine().describe_context(), data,
584 			!(data & 0x01) ? " FOCSON" : "",
585 			!(data & 0x02) ? " SPDLRUN" : "",
586 			!(data & 0x04) ? " JUMPTRIG" : "",
587 			!(data & 0x08) ? string_format(" SCANA (%c %c)", (data & 0x10) ? 'L' : 'H', (data & 0x20) ? 'F' : 'R') : "",
588 			(data & 0x40) ? " LASERON" : "",
589 			!(data & 0x80) ? " SYNCST0" : "");
590 	}
591 
592 	// bit 5 selects the direction of slider movement for JUMP TRG and scanning
593 	int direction = (data & 0x20) ? 1 : -1;
594 
595 	// on the falling edge of bit 2, jump one track in either direction
596 	if (!(data & 0x04) && (prev & 0x04))
597 		advance_slider(direction);
598 
599 	// bit 3 low enables scanning
600 	if (!(data & 0x08))
601 	{
602 		// bit 4 selects the speed
603 		int delta = (data & 0x10) ? SCAN_SPEED : SEEK_FAST_SPEED;
604 		set_slider_speed(delta * direction);
605 	}
606 
607 	// bit 3 high stops scanning
608 	else
609 		set_slider_speed(0);
610 }
611 
612 
613 //-------------------------------------------------
614 //  ppi1_portc_w - handle writes to port C of
615 //  PPI #1
616 //-------------------------------------------------
617 
ppi1_portc_w(uint8_t data)618 void pioneer_ldv1000_device::ppi1_portc_w(uint8_t data)
619 {
620 	/*
621 	    $01 = AUD 1
622 	    $02 = AUD 2
623 	    $04 = AUDIO ENABLE
624 	    $08 = /VIDEO SQ
625 	    $10 = COMMAND
626 	    $20 = STATUS
627 	    $40 = SIZE 8/12
628 	    $80 = /LED CAV
629 	*/
630 
631 	// set the new value
632 	uint8_t prev = m_portc1;
633 	m_portc1 = data;
634 	if (LOG_PORT_IO && ((data ^ prev) & 0xcf) != 0)
635 	{
636 		logerror("%s:PORTC.1=%02X%s%s%s%s%s%s%s%s\n", machine().describe_context(), data,
637 			(data & 0x01) ? " AUD1" : "",
638 			(data & 0x02) ? " AUD2" : "",
639 			(data & 0x04) ? " AUDEN" : "",
640 			!(data & 0x08) ? " VIDEOSQ" : "",
641 			(data & 0x10) ? " COMMAND" : "",
642 			(data & 0x20) ? " STATUS" : "",
643 			(data & 0x40) ? " SIZE8" : "",
644 			!(data & 0x80) ? " CAV" : "");
645 	}
646 
647 	// bit 4 sends a command strobe signal to Host CPU
648 	m_command_strobe_cb(bool(data & 0x10));
649 
650 	// video squelch is controlled by bit 3
651 	set_video_squelch((data & 0x08) == 0);
652 
653 	// audio squelch is controlled by bits 0-2
654 	set_audio_squelch(!(data & 0x04) || !(data & 0x01), !(data & 0x04) || !(data & 0x02));
655 }
656