Home | History | Annotate | Line # | Download | only in vr
vrkiu.c revision 1.1
      1 /*	$NetBSD: vrkiu.c,v 1.1 1999/09/16 12:23:33 takemura Exp $	*/
      2 
      3 /*-
      4  * Copyright (c) 1999 SASAKI Takesi
      5  * Copyright (c) 1999 PocketBSD Project. All rights reserved.
      6  * All rights reserved.
      7  *
      8  * This code is a part of the PocketBSD.
      9  *
     10  * Redistribution and use in source and binary forms, with or without
     11  * modification, are permitted provided that the following conditions
     12  * are met:
     13  * 1. Redistributions of source code must retain the above copyright
     14  *    notice, this list of conditions and the following disclaimer.
     15  * 2. Redistributions in binary form must reproduce the above copyright
     16  *    notice, this list of conditions and the following disclaimer in the
     17  *    documentation and/or other materials provided with the distribution.
     18  * 3. All advertising materials mentioning features or use of this software
     19  *    must display the following acknowledgement:
     20  *	This product includes software developed by the PocketBSD project
     21  *	and its contributors.
     22  * 4. Neither the name of the project nor the names of its contributors
     23  *    may be used to endorse or promote products derived from this software
     24  *    without specific prior written permission.
     25  *
     26  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
     27  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     28  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     29  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
     30  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     31  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
     32  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     33  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     34  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     35  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     36  * SUCH DAMAGE.
     37  *
     38  */
     39 
     40 #define VRKIUDEBUG
     41 
     42 #include <sys/param.h>
     43 #include <sys/tty.h>
     44 #include <sys/systm.h>
     45 #include <sys/device.h>
     46 #include <sys/conf.h>
     47 #include <sys/kernel.h>
     48 #include <sys/proc.h>
     49 
     50 #include <machine/intr.h>
     51 #include <machine/cpu.h>
     52 #include <machine/bus.h>
     53 #include <machine/platid.h>
     54 
     55 #include <hpcmips/vr/vr.h>
     56 #include <hpcmips/vr/vripvar.h>
     57 #include <hpcmips/vr/vrkiuvar.h>
     58 #include <hpcmips/vr/vrkiureg.h>
     59 #include <hpcmips/vr/icureg.h>
     60 
     61 #ifdef VRKIUDEBUG
     62 int vrkiu_debug = 0;
     63 #define DPRINTF(arg) if (vrkiu_debug) printf arg;
     64 #else
     65 #define	DPRINTF(arg)
     66 #endif
     67 
     68 static int vrkiumatch __P((struct device *, struct cfdata *, void *));
     69 static void vrkiuattach __P((struct device *, struct device *, void *));
     70 
     71 static void vrkiu_write __P((struct vrkiu_softc *, int, unsigned short));
     72 static unsigned short vrkiu_read __P((struct vrkiu_softc *, int));
     73 
     74 int vrkiu_intr __P((void *));
     75 
     76 static void detect_key __P((struct vrkiu_softc *));
     77 static void process_key __P((struct vrkiu_softc *, int, int));
     78 
     79 struct cfattach vrkiu_ca = {
     80 	sizeof(struct vrkiu_softc), vrkiumatch, vrkiuattach
     81 };
     82 
     83 static struct vrkiu_softc *the_vrkiu = NULL; /* XXX: kludge!! */
     84 
     85 /* XXX: This tranlation table may depend on each machine.
     86         Should I build it in? */
     87 static char keytrans[] = {
     88 	-1,  28,  25,  52,  21,  48,  44,  57,  /* - enter p . y b z space */
     89 	-1,  53,  24,  51,  20,  47,  30,  -1,  /* - / o , t v a - */
     90 	-1,  -1,  23,  50,  19,  46,  17,  -1,  /* - - i m r c w - */
     91 	13,  -1,  22,  -1,  18,  45,  16,   2,  /* = - u - e x q 1 */
     92 	-1,  -1,  11,  38,  40,  34,  15,  59,  /* - - 0 l ' g tab f1 */
     93 	-1,  39,  10,  49,   6,  33,   3,  37,  /* - ; 9 n 5 f 2 k */
     94 	-1,  27,   9,  36,   5,  32,   7,  -1,  /* - ] 8 j 4 d 6 - */
     95 	12,  26,   8,  35,   4,  41,  31,  -1,  /* - [ 7 h 3 ` s - */
     96 	58,  -1,  -1,  -1,  14,  -1,  66,  61,  /* caps - - - bs - f8 f3 */
     97 	-1,  56,  -1,  -1,  43,  -1,  65,  62,  /* - alt - - \ - f7 f4 */
     98 	-1,  -1,  29,  -1,  68,  -1,  64,  60,  /* - - ctrl - f10 - f6 f2 */
     99 	-1,  -1,  -1,  42,  -1,  67,  63,   1,  /* - - - shift - f9 f5 esc */
    100 };
    101 /* XXX: fill the field of funct. keys, ex. arrow, fnc, nfer... */
    102 
    103 #define	SCROLL		0x0001	/* stop output */
    104 #define	NUM		0x0002	/* numeric shift  cursors vs. numeric */
    105 #define	CAPS		0x0004	/* caps shift -- swaps case of letter */
    106 #define	SHIFT		0x0008	/* keyboard shift */
    107 #define	CTL		0x0010	/* control shift  -- allows ctl function */
    108 #define	ASCII		0x0020	/* ascii code for this key */
    109 #define	ALT		0x0080	/* alternate shift -- alternate chars */
    110 #define	FUNC		0x0100	/* function key */
    111 #define	KP		0x0200	/* Keypad keys */
    112 #define	NONE		0x0400	/* no function */
    113 
    114 #define	CODE_SIZE	4		/* Use a max of 4 for now... */
    115 
    116 typedef struct {
    117 	u_short	type;
    118 	char unshift[CODE_SIZE];
    119 	char shift[CODE_SIZE];
    120 	char ctl[CODE_SIZE];
    121 } Scan_def;
    122 
    123 #if !defined(PCCONS_REAL_BS)
    124 #define PCCONS_REAL_BS 1
    125 #endif
    126 #if !defined(CAPS_IS_CONTROL)
    127 #define CAPS_IS_CONTROL 1
    128 #endif
    129 #if !defined(CAPS_ADD_CONTROL)
    130 #define CAPS_ADD_CONTROL 0
    131 #endif
    132 
    133 static Scan_def	scan_codes[] = {
    134 	{ NONE,	"",		"",		"" },		/* 0 unused */
    135 	{ ASCII,"\033",		"\033",		"\033" },	/* 1 ESCape */
    136 	{ ASCII,"1",		"!",		"!" },		/* 2 1 */
    137 	{ ASCII,"2",		"@",		"\000" },	/* 3 2 */
    138 	{ ASCII,"3",		"#",		"#" },		/* 4 3 */
    139 	{ ASCII,"4",		"$",		"$" },		/* 5 4 */
    140 	{ ASCII,"5",		"%",		"%" },		/* 6 5 */
    141 	{ ASCII,"6",		"^",		"\036" },	/* 7 6 */
    142 	{ ASCII,"7",		"&",		"&" },		/* 8 7 */
    143 	{ ASCII,"8",		"*",		"\010" },	/* 9 8 */
    144 	{ ASCII,"9",		"(",		"(" },		/* 10 9 */
    145 	{ ASCII,"0",		")",		")" },		/* 11 0 */
    146 	{ ASCII,"-",		"_",		"\037" },	/* 12 - */
    147 	{ ASCII,"=",		"+",		"+" },		/* 13 = */
    148 #ifndef PCCONS_REAL_BS
    149 	{ ASCII,"\177",		"\177",		"\010" },	/* 14 backspace */
    150 #else
    151 	{ ASCII,"\010",		"\010",		"\177" },	/* 14 backspace */
    152 #endif
    153 	{ ASCII,"\t",		"\177\t",	"\t" },		/* 15 tab */
    154 	{ ASCII,"q",		"Q",		"\021" },	/* 16 q */
    155 	{ ASCII,"w",		"W",		"\027" },	/* 17 w */
    156 	{ ASCII,"e",		"E",		"\005" },	/* 18 e */
    157 	{ ASCII,"r",		"R",		"\022" },	/* 19 r */
    158 	{ ASCII,"t",		"T",		"\024" },	/* 20 t */
    159 	{ ASCII,"y",		"Y",		"\031" },	/* 21 y */
    160 	{ ASCII,"u",		"U",		"\025" },	/* 22 u */
    161 	{ ASCII,"i",		"I",		"\011" },	/* 23 i */
    162 	{ ASCII,"o",		"O",		"\017" },	/* 24 o */
    163 	{ ASCII,"p",		"P",		"\020" },	/* 25 p */
    164 	{ ASCII,"[",		"{",		"\033" },	/* 26 [ */
    165 	{ ASCII,"]",		"}",		"\035" },	/* 27 ] */
    166 	{ ASCII,"\r",		"\r",		"\n" },		/* 28 return */
    167 #if CAPS_IS_CONTROL == 1 && CAPS_ADD_CONTROL == 0
    168 	{ CAPS,	"",		"",		"" },		/* 29 caps */
    169 #else
    170 	{ CTL,	"",		"",		"" },		/* 29 control */
    171 #endif
    172 	{ ASCII,"a",		"A",		"\001" },	/* 30 a */
    173 	{ ASCII,"s",		"S",		"\023" },	/* 31 s */
    174 	{ ASCII,"d",		"D",		"\004" },	/* 32 d */
    175 	{ ASCII,"f",		"F",		"\006" },	/* 33 f */
    176 	{ ASCII,"g",		"G",		"\007" },	/* 34 g */
    177 	{ ASCII,"h",		"H",		"\010" },	/* 35 h */
    178 	{ ASCII,"j",		"J",		"\n" },		/* 36 j */
    179 	{ ASCII,"k",		"K",		"\013" },	/* 37 k */
    180 	{ ASCII,"l",		"L",		"\014" },	/* 38 l */
    181 	{ ASCII,";",		":",		";" },		/* 39 ; */
    182 	{ ASCII,"'",		"\"",		"'" },		/* 40 ' */
    183 	{ ASCII,"`",		"~",		"`" },		/* 41 ` */
    184 	{ SHIFT,"",		"",		"" },		/* 42 shift */
    185 	{ ASCII,"\\",		"|",		"\034" },	/* 43 \ */
    186 	{ ASCII,"z",		"Z",		"\032" },	/* 44 z */
    187 	{ ASCII,"x",		"X",		"\030" },	/* 45 x */
    188 	{ ASCII,"c",		"C",		"\003" },	/* 46 c */
    189 	{ ASCII,"v",		"V",		"\026" },	/* 47 v */
    190 	{ ASCII,"b",		"B",		"\002" },	/* 48 b */
    191 	{ ASCII,"n",		"N",		"\016" },	/* 49 n */
    192 	{ ASCII,"m",		"M",		"\r" },		/* 50 m */
    193 	{ ASCII,",",		"<",		"<" },		/* 51 , */
    194 	{ ASCII,".",		">",		">" },		/* 52 . */
    195 	{ ASCII,"/",		"?",		"\037" },	/* 53 / */
    196 	{ SHIFT,"",		"",		"" },		/* 54 shift */
    197 	{ KP,	"*",		"*",		"*" },		/* 55 kp * */
    198 	{ ALT,	"",		"",		"" },		/* 56 alt */
    199 	{ ASCII," ",		" ",		"\000" },	/* 57 space */
    200 #if CAPS_IS_CONTROL == 1 || CAPS_ADD_CONTROL == 1
    201 	{ CTL,	"",		"",		"" },		/* 58 control */
    202 #else
    203 	{ CAPS,	"",		"",		"" },		/* 58 caps */
    204 #endif
    205 	{ FUNC,	"\033[M",	"\033[Y",	"\033[k" },	/* 59 f1 */
    206 	{ FUNC,	"\033[N",	"\033[Z",	"\033[l" },	/* 60 f2 */
    207 	{ FUNC,	"\033[O",	"\033[a",	"\033[m" },	/* 61 f3 */
    208 	{ FUNC,	"\033[P",	"\033[b",	"\033[n" },	/* 62 f4 */
    209 	{ FUNC,	"\033[Q",	"\033[c",	"\033[o" },	/* 63 f5 */
    210 	{ FUNC,	"\033[R",	"\033[d",	"\033[p" },	/* 64 f6 */
    211 	{ FUNC,	"\033[S",	"\033[e",	"\033[q" },	/* 65 f7 */
    212 	{ FUNC,	"\033[T",	"\033[f",	"\033[r" },	/* 66 f8 */
    213 	{ FUNC,	"\033[U",	"\033[g",	"\033[s" },	/* 67 f9 */
    214 	{ FUNC,	"\033[V",	"\033[h",	"\033[t" },	/* 68 f10 */
    215 	{ NUM,	"",		"",		"" },		/* 69 num lock */
    216 	{ SCROLL,"",		"",		"" },		/* 70 scroll lock */
    217 	{ KP,	"7",		"\033[H",	"7" },		/* 71 kp 7 */
    218 	{ KP,	"8",		"\033[A",	"8" },		/* 72 kp 8 */
    219 	{ KP,	"9",		"\033[I",	"9" },		/* 73 kp 9 */
    220 	{ KP,	"-",		"-",		"-" },		/* 74 kp - */
    221 	{ KP,	"4",		"\033[D",	"4" },		/* 75 kp 4 */
    222 	{ KP,	"5",		"\033[E",	"5" },		/* 76 kp 5 */
    223 	{ KP,	"6",		"\033[C",	"6" },		/* 77 kp 6 */
    224 	{ KP,	"+",		"+",		"+" },		/* 78 kp + */
    225 	{ KP,	"1",		"\033[F",	"1" },		/* 79 kp 1 */
    226 	{ KP,	"2",		"\033[B",	"2" },		/* 80 kp 2 */
    227 	{ KP,	"3",		"\033[G",	"3" },		/* 81 kp 3 */
    228 	{ KP,	"0",		"\033[L",	"0" },		/* 82 kp 0 */
    229 	{ KP,	".",		"\177",		"." },		/* 83 kp . */
    230 	{ NONE,	"",		"",		"" },		/* 84 0 */
    231 	{ NONE,	"100",		"",		"" },		/* 85 0 */
    232 	{ NONE,	"101",		"",		"" },		/* 86 0 */
    233 	{ FUNC,	"\033[W",	"\033[i",	"\033[u" },	/* 87 f11 */
    234 	{ FUNC,	"\033[X",	"\033[j",	"\033[v" },	/* 88 f12 */
    235 	{ NONE,	"102",		"",		"" },		/* 89 0 */
    236 	{ NONE,	"103",		"",		"" },		/* 90 0 */
    237 	{ NONE,	"",		"",		"" },		/* 91 0 */
    238 	{ ASCII,"\177",		"\177",		"\010" },	/* 92 del */
    239 	{ NONE,	"",		"",		"" },		/* 93 0 */
    240 	{ NONE,	"",		"",		"" },		/* 94 0 */
    241 	{ NONE,	"",		"",		"" },		/* 95 0 */
    242 	{ NONE,	"",		"",		"" },		/* 96 0 */
    243 	{ NONE,	"",		"",		"" },		/* 97 0 */
    244 	{ NONE,	"",		"",		"" },		/* 98 0 */
    245 	{ NONE,	"",		"",		"" },		/* 99 0 */
    246 	{ NONE,	"",		"",		"" },		/* 100 */
    247 	{ NONE,	"",		"",		"" },		/* 101 */
    248 	{ NONE,	"",		"",		"" },		/* 102 */
    249 	{ NONE,	"",		"",		"" },		/* 103 */
    250 	{ NONE,	"",		"",		"" },		/* 104 */
    251 	{ NONE,	"",		"",		"" },		/* 105 */
    252 	{ NONE,	"",		"",		"" },		/* 106 */
    253 	{ NONE,	"",		"",		"" },		/* 107 */
    254 	{ NONE,	"",		"",		"" },		/* 108 */
    255 	{ NONE,	"",		"",		"" },		/* 109 */
    256 	{ NONE,	"",		"",		"" },		/* 110 */
    257 	{ NONE,	"",		"",		"" },		/* 111 */
    258 	{ NONE,	"",		"",		"" },		/* 112 */
    259 	{ NONE,	"",		"",		"" },		/* 113 */
    260 	{ NONE,	"",		"",		"" },		/* 114 */
    261 	{ NONE,	"",		"",		"" },		/* 115 */
    262 	{ NONE,	"",		"",		"" },		/* 116 */
    263 	{ NONE,	"",		"",		"" },		/* 117 */
    264 	{ NONE,	"",		"",		"" },		/* 118 */
    265 	{ NONE,	"",		"",		"" },		/* 119 */
    266 	{ NONE,	"",		"",		"" },		/* 120 */
    267 	{ NONE,	"",		"",		"" },		/* 121 */
    268 	{ NONE,	"",		"",		"" },		/* 122 */
    269 	{ NONE,	"",		"",		"" },		/* 123 */
    270 	{ NONE,	"",		"",		"" },		/* 124 */
    271 	{ NONE,	"",		"",		"" },		/* 125 */
    272 	{ NONE,	"",		"",		"" },		/* 126 */
    273 	{ NONE,	"",		"",		"" },		/* 127 */
    274 };
    275 
    276 /* XXX: Make these queue obsolute, or move these into vrkiu_softc */
    277 static int
    278 vrkiumatch(parent, cf, aux)
    279 	struct device *parent;
    280 	struct cfdata *cf;
    281 	void *aux;
    282 {
    283 	return 1;		/* XXX */
    284 }
    285 
    286 static inline void
    287 vrkiu_write(sc, port, val)
    288 	struct vrkiu_softc *sc;
    289 	int port;
    290 	unsigned short val;
    291 {
    292 	bus_space_write_2(sc->sc_iot, sc->sc_ioh, port, val);
    293 }
    294 
    295 static inline unsigned short
    296 vrkiu_read(sc, port)
    297 	struct vrkiu_softc *sc;
    298 	int port;
    299 {
    300 	return bus_space_read_2(sc->sc_iot, sc->sc_ioh, port);
    301 }
    302 
    303 static void
    304 vrkiuattach(parent, self, aux)
    305 	struct device *parent;
    306 	struct device *self;
    307 	void *aux;
    308 {
    309 	struct vrkiu_softc *sc = (struct vrkiu_softc *)self;
    310 	struct vrip_attach_args *va = aux;
    311 
    312 	bus_space_tag_t iot = va->va_iot;
    313 	bus_space_handle_t ioh;
    314 
    315 	if (bus_space_map(iot, va->va_addr, 1, 0, &ioh)) {
    316 		printf(": can't map bus space\n");
    317 		return;
    318 	}
    319 
    320 	sc->sc_iot = iot;
    321 	sc->sc_ioh = ioh;
    322 
    323 	/* set KIU */
    324 	vrkiu_write(sc, KIURST, 1);   /* reset */
    325 	vrkiu_write(sc, KIUSCANLINE, 0); /* 96keys */
    326 	vrkiu_write(sc, KIUWKS, 0x18a4); /* XXX: scan timing! */
    327 	vrkiu_write(sc, KIUWKI, 450);
    328 	vrkiu_write(sc, KIUSCANREP, 0x8023);
    329 				/* KEYEN | STPREP = 2 | ATSTP | ATSCAN */
    330 
    331 	the_vrkiu = sc;
    332 
    333 	if (!(sc->sc_handler =
    334 	      vrip_intr_establish(va->va_vc, va->va_intr, IPL_TTY,
    335 				  vrkiu_intr, sc))) {
    336 		printf (": can't map interrupt line.\n");
    337 		return;
    338 	}
    339 	/* Level2 register setting */
    340 	vrip_intr_setmask2(va->va_vc, sc->sc_handler, KIUINT_KDATRDY, 1);
    341 
    342 	printf("\n");
    343 }
    344 
    345 int
    346 vrkiu_intr(arg)
    347 	void *arg;
    348 {
    349         struct vrkiu_softc *sc = arg;
    350 	/* When key scan finisshed, this entry is called. */
    351 	detect_key(sc);
    352 	DPRINTF(("%d", vrkiu_read(sc, KIUINT) & 7));
    353 
    354 	vrkiu_write(sc, KIUINT, 0x7); /* Clear all interrupt */
    355 
    356 	return 0;
    357 }
    358 
    359 static void
    360 detect_key(sc)
    361 	struct vrkiu_softc *sc;
    362 {
    363 	int i, k;
    364 
    365 	DPRINTF(("[detect_key():begin read]"));
    366 
    367 	for (i = 0; i < KIU_NSCANLINE / 2; i++) {
    368 		k = vrkiu_read(sc, KIUDATP + i * 2) ^ sc->keystat[i];
    369 		sc->keystat[i] = vrkiu_read(sc, KIUDATP + i * 2);
    370 		while (k) {
    371 			int n, m;
    372 			n = ffs(k) - 1;
    373 			if (n < 0) {
    374 				break;
    375 			}
    376 			k ^= 1 << n;
    377 			m = n + i * 16;
    378 			if (keytrans[m] < 0) {
    379                                 printf("vrkiu: Unkown scan code 0x%02x\n", m);
    380                                 continue;
    381 			}
    382 			/* XXX: scanbuf may overflow! */
    383 			process_key(sc, keytrans[m],
    384 				    !((sc->keystat[i] & (1 << n))));
    385 		}
    386 		/* XXX: The order of keys can be a problem.
    387 		   If CTRL and normal key are pushed simultaneously,
    388 		   normal key can be entered in queue first.
    389 		   Same problem would occur in key break. */
    390 	}
    391 	/* TODO: Enter scancode into the queue as long as the key pressed */
    392 
    393 }
    394 
    395 static void
    396 process_key(sc, k, brk)
    397 	struct vrkiu_softc *sc;
    398 	int k, brk;
    399 {
    400 	char *p;
    401 	extern struct tty biconsdev_tty[];
    402 
    403 	switch (scan_codes[k].type) {
    404 	case ALT:
    405 		sc->k_alt = brk ? 0 : 1;
    406 		break;
    407 	case SHIFT:
    408 		sc->k_sft = brk ? 0 : 1;
    409 		break;
    410 	case CTL:
    411 		sc->k_ctrl = brk ? 0 : 1;
    412 		break;
    413 	case ASCII:
    414 		if (!brk) {
    415 			if (sc->k_ctrl) {
    416 				p = scan_codes[k].ctl;
    417 			} else if (sc->k_sft) {
    418 				p = scan_codes[k].shift;
    419 			} else {
    420 				p = scan_codes[k].unshift;
    421 			}
    422 			sc->keybuf[sc->keybufhead++] =
    423 				sc->k_alt ? (*p | 0x80) : *p;
    424 				/* XXX: multi byte key! */
    425 			if (sc->keybufhead >= NKEYBUF) {
    426 				sc->keybufhead = 0;
    427 			}
    428 			(*linesw[0].l_rint)(sc->k_alt ? (*p | 0x80) : *p,
    429 					    &biconsdev_tty [0]);
    430 		}
    431 		break;
    432 	default:
    433 		/* Ignored */
    434 	}
    435 }
    436 
    437 /* called from biconsdev.c */
    438 int
    439 vrkiu_getc()
    440 {
    441 	int ret;
    442 
    443 	if (the_vrkiu == NULL) {
    444 		return 0;	/* XXX */
    445 	}
    446 
    447 	while (the_vrkiu->keybuftail == the_vrkiu->keybufhead) {
    448 		detect_key(the_vrkiu);
    449 	}
    450 	ret = the_vrkiu->keybuf[the_vrkiu->keybuftail++];
    451 	if (the_vrkiu->keybuftail >= NKEYBUF) {
    452 		the_vrkiu->keybuftail = 0;
    453 	}
    454 	return ret;
    455 }
    456 
    457