1 /* $NetBSD: fpu.c,v 1.13 2001/09/22 19:58:28 eeh Exp $ */ 2 3 /* 4 * Copyright (c) 1992, 1993 5 * The Regents of the University of California. All rights reserved. 6 * 7 * This software was developed by the Computer Systems Engineering group 8 * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and 9 * contributed to Berkeley. 10 * 11 * All advertising materials mentioning features or use of this software 12 * must display the following acknowledgement: 13 * This product includes software developed by the University of 14 * California, Lawrence Berkeley Laboratory. 15 * 16 * Redistribution and use in source and binary forms, with or without 17 * modification, are permitted provided that the following conditions 18 * are met: 19 * 1. Redistributions of source code must retain the above copyright 20 * notice, this list of conditions and the following disclaimer. 21 * 2. Redistributions in binary form must reproduce the above copyright 22 * notice, this list of conditions and the following disclaimer in the 23 * documentation and/or other materials provided with the distribution. 24 * 3. All advertising materials mentioning features or use of this software 25 * must display the following acknowledgement: 26 * This product includes software developed by the University of 27 * California, Berkeley and its contributors. 28 * 4. Neither the name of the University nor the names of its contributors 29 * may be used to endorse or promote products derived from this software 30 * without specific prior written permission. 31 * 32 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 33 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 34 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 35 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 36 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 37 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 38 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 39 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 40 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 41 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 42 * SUCH DAMAGE. 43 * 44 * @(#)fpu.c 8.1 (Berkeley) 6/11/93 45 */ 46 47 #include <sys/param.h> 48 #include <sys/proc.h> 49 #include <sys/signal.h> 50 #include <sys/systm.h> 51 #include <sys/syslog.h> 52 #include <sys/signalvar.h> 53 54 #include <machine/instr.h> 55 #include <machine/reg.h> 56 57 #include <sparc/fpu/fpu_emu.h> 58 #include <sparc/fpu/fpu_extern.h> 59 60 int fpe_debug = 0; 61 62 #ifdef DEBUG 63 /* 64 * Dump a `fpn' structure. 65 */ 66 void 67 fpu_dumpfpn(struct fpn *fp) 68 { 69 static char *class[] = { 70 "SNAN", "QNAN", "ZERO", "NUM", "INF" 71 }; 72 73 printf("%s %c.%x %x %x %xE%d", class[fp->fp_class + 2], 74 fp->fp_sign ? '-' : ' ', 75 fp->fp_mant[0], fp->fp_mant[1], 76 fp->fp_mant[2], fp->fp_mant[3], 77 fp->fp_exp); 78 } 79 #endif 80 81 /* 82 * fpu_execute returns the following error numbers (0 = no error): 83 */ 84 #define FPE 1 /* take a floating point exception */ 85 #define NOTFPU 2 /* not an FPU instruction */ 86 87 /* 88 * Translate current exceptions into `first' exception. The 89 * bits go the wrong way for ffs() (0x10 is most important, etc). 90 * There are only 5, so do it the obvious way. 91 */ 92 #define X1(x) x 93 #define X2(x) x,x 94 #define X4(x) x,x,x,x 95 #define X8(x) X4(x),X4(x) 96 #define X16(x) X8(x),X8(x) 97 98 static char cx_to_trapx[] = { 99 X1(FSR_NX), 100 X2(FSR_DZ), 101 X4(FSR_UF), 102 X8(FSR_OF), 103 X16(FSR_NV) 104 }; 105 static u_char fpu_codes[] = { 106 X1(FPE_FLTINEX_TRAP), 107 X2(FPE_FLTDIV_TRAP), 108 X4(FPE_FLTUND_TRAP), 109 X8(FPE_FLTOVF_TRAP), 110 X16(FPE_FLTOPERR_TRAP) 111 }; 112 113 /* 114 * The FPU gave us an exception. Clean up the mess. Note that the 115 * fp queue can only have FPops in it, never load/store FP registers 116 * nor FBfcc instructions. Experiments with `crashme' prove that 117 * unknown FPops do enter the queue, however. 118 */ 119 void 120 fpu_cleanup(p, fs) 121 register struct proc *p; 122 #ifndef SUN4U 123 register struct fpstate *fs; 124 #else /* SUN4U */ 125 register struct fpstate64 *fs; 126 #endif /* SUN4U */ 127 { 128 register int i, fsr = fs->fs_fsr, error; 129 union instr instr; 130 struct fpemu fe; 131 132 switch ((fsr >> FSR_FTT_SHIFT) & FSR_FTT_MASK) { 133 134 case FSR_TT_NONE: 135 panic("fpu_cleanup: No fault"); /* ??? */ 136 break; 137 138 case FSR_TT_IEEE: 139 /* XXX missing trap address! */ 140 if ((i = fsr & FSR_CX) == 0) 141 panic("fpu ieee trap, but no exception"); 142 trapsignal(p, SIGFPE, fpu_codes[i - 1]); 143 break; /* XXX should return, but queue remains */ 144 145 case FSR_TT_UNFIN: 146 #ifdef SUN4U 147 if (fs->fs_qsize == 0) { 148 printf("fpu_cleanup: unfinished fpop"); 149 /* The book sez reexecute or emulate. */ 150 return; 151 } 152 break; 153 154 #endif /* SUN4U */ 155 case FSR_TT_UNIMP: 156 if (fs->fs_qsize == 0) 157 panic("fpu_cleanup: unimplemented fpop"); 158 break; 159 160 case FSR_TT_SEQ: 161 panic("fpu sequence error"); 162 /* NOTREACHED */ 163 164 case FSR_TT_HWERR: 165 log(LOG_ERR, "fpu hardware error (%s[%d])\n", 166 p->p_comm, p->p_pid); 167 uprintf("%s[%d]: fpu hardware error\n", p->p_comm, p->p_pid); 168 trapsignal(p, SIGFPE, -1); /* ??? */ 169 goto out; 170 171 default: 172 printf("fsr=0x%x\n", fsr); 173 panic("fpu error"); 174 } 175 176 /* emulate the instructions left in the queue */ 177 fe.fe_fpstate = fs; 178 for (i = 0; i < fs->fs_qsize; i++) { 179 instr.i_int = fs->fs_queue[i].fq_instr; 180 if (instr.i_any.i_op != IOP_reg || 181 (instr.i_op3.i_op3 != IOP3_FPop1 && 182 instr.i_op3.i_op3 != IOP3_FPop2)) 183 panic("bogus fpu queue"); 184 error = fpu_execute(&fe, instr); 185 switch (error) { 186 187 case 0: 188 continue; 189 190 case FPE: 191 trapsignal(p, SIGFPE, 192 fpu_codes[(fs->fs_fsr & FSR_CX) - 1]); 193 break; 194 195 case NOTFPU: 196 #ifdef SUN4U 197 #ifdef DEBUG 198 printf("fpu_cleanup: not an FPU error -- sending SIGILL\n"); 199 Debugger(); 200 #endif 201 #endif /* SUN4U */ 202 trapsignal(p, SIGILL, 0); /* ??? code? */ 203 break; 204 205 default: 206 panic("fpu_cleanup 3"); 207 /* NOTREACHED */ 208 } 209 /* XXX should stop here, but queue remains */ 210 } 211 out: 212 fs->fs_qsize = 0; 213 } 214 215 #ifdef notyet 216 /* 217 * If we have no FPU at all (are there any machines like this out 218 * there!?) we have to emulate each instruction, and we need a pointer 219 * to the trapframe so that we can step over them and do FBfcc's. 220 * We know the `queue' is empty, though; we just want to emulate 221 * the instruction at tf->tf_pc. 222 */ 223 fpu_emulate(p, tf, fs) 224 struct proc *p; 225 register struct trapframe *tf; 226 #ifndef SUN4U 227 register struct fpstate *fs; 228 #else /* SUN4U */ 229 register struct fpstate64 *fs; 230 #endif /* SUN4U */ 231 { 232 233 do { 234 fetch instr from pc 235 decode 236 if (integer instr) { 237 /* 238 * We do this here, rather than earlier, to avoid 239 * losing even more badly than usual. 240 */ 241 if (p->p_addr->u_pcb.pcb_uw) { 242 write_user_windows(); 243 if (rwindow_save(p)) 244 sigexit(p, SIGILL); 245 } 246 if (loadstore) { 247 do_it; 248 pc = npc, npc += 4 249 } else if (fbfcc) { 250 do_annul_stuff; 251 } else 252 return; 253 } else if (fpu instr) { 254 fe.fe_fsr = fs->fs_fsr &= ~FSR_CX; 255 error = fpu_execute(&fe, fs, instr); 256 switch (error) { 257 etc; 258 } 259 } else 260 return; 261 if (want to reschedule) 262 return; 263 } while (error == 0); 264 } 265 #endif 266 267 /* 268 * Execute an FPU instruction (one that runs entirely in the FPU; not 269 * FBfcc or STF, for instance). On return, fe->fe_fs->fs_fsr will be 270 * modified to reflect the setting the hardware would have left. 271 * 272 * Note that we do not catch all illegal opcodes, so you can, for instance, 273 * multiply two integers this way. 274 */ 275 int 276 fpu_execute(fe, instr) 277 register struct fpemu *fe; 278 union instr instr; 279 { 280 register struct fpn *fp; 281 #ifndef SUN4U 282 register int opf, rs1, rs2, rd, type, mask, fsr, cx; 283 register struct fpstate *fs; 284 #else /* SUN4U */ 285 register int opf, rs1, rs2, rd, type, mask, fsr, cx, i, cond; 286 register struct fpstate64 *fs; 287 #endif /* SUN4U */ 288 u_int space[4]; 289 290 /* 291 * `Decode' and execute instruction. Start with no exceptions. 292 * The type of any i_opf opcode is in the bottom two bits, so we 293 * squish them out here. 294 */ 295 opf = instr.i_opf.i_opf; 296 /* 297 * The low two bits of the opf field for floating point insns usually 298 * correspond to the operation width: 299 * 300 * 0: Invalid 301 * 1: Single precision float 302 * 2: Double precision float 303 * 3: Quad precision float 304 * 305 * The exceptions are the integer to float conversion instructions. 306 * 307 * For double and quad precision, the low bit if the rs or rd field 308 * is actually the high bit of the register number. 309 */ 310 311 type = opf & 3; 312 mask = 0x3 >> (3 - type); 313 314 rs1 = instr.i_opf.i_rs1; 315 rs1 = (rs1 & ~mask) | ((rs1 & mask & 0x1) << 5); 316 rs2 = instr.i_opf.i_rs2; 317 rs2 = (rs2 & ~mask) | ((rs2 & mask & 0x1) << 5); 318 rd = instr.i_opf.i_rd; 319 rd = (rd & ~mask) | ((rd & mask & 0x1) << 5); 320 #ifdef DIAGNOSTIC 321 if ((rs1 | rs2 | rd) & mask) 322 /* This may be an FPU insn but it is illegal. */ 323 return (NOTFPU); 324 #endif 325 fs = fe->fe_fpstate; 326 fe->fe_fsr = fs->fs_fsr & ~FSR_CX; 327 fe->fe_cx = 0; 328 #ifdef SUN4U 329 /* 330 * Check to see if we're dealing with a fancy cmove and handle 331 * it first. 332 */ 333 if (instr.i_op3.i_op3 == IOP3_FPop2 && (opf&0xff0) != (FCMP&0xff0)) { 334 switch (opf >>= 2) { 335 case FMVFC0 >> 2: 336 DPRINTF(FPE_INSN, ("fpu_execute: FMVFC0\n")); 337 cond = (fs->fs_fsr>>FSR_FCC_SHIFT)&FSR_FCC_MASK; 338 if (instr.i_fmovcc.i_cond != cond) return(0); /* success */ 339 rs1 = fs->fs_regs[rs2]; 340 goto mov; 341 case FMVFC1 >> 2: 342 DPRINTF(FPE_INSN, ("fpu_execute: FMVFC1\n")); 343 cond = (fs->fs_fsr>>FSR_FCC1_SHIFT)&FSR_FCC_MASK; 344 if (instr.i_fmovcc.i_cond != cond) return(0); /* success */ 345 rs1 = fs->fs_regs[rs2]; 346 goto mov; 347 case FMVFC2 >> 2: 348 DPRINTF(FPE_INSN, ("fpu_execute: FMVFC2\n")); 349 cond = (fs->fs_fsr>>FSR_FCC2_SHIFT)&FSR_FCC_MASK; 350 if (instr.i_fmovcc.i_cond != cond) return(0); /* success */ 351 rs1 = fs->fs_regs[rs2]; 352 goto mov; 353 case FMVFC3 >> 2: 354 DPRINTF(FPE_INSN, ("fpu_execute: FMVFC3\n")); 355 cond = (fs->fs_fsr>>FSR_FCC3_SHIFT)&FSR_FCC_MASK; 356 if (instr.i_fmovcc.i_cond != cond) return(0); /* success */ 357 rs1 = fs->fs_regs[rs2]; 358 goto mov; 359 case FMVIC >> 2: 360 /* Presume we're curproc */ 361 DPRINTF(FPE_INSN, ("fpu_execute: FMVIC\n")); 362 cond = (curproc->p_md.md_tf->tf_tstate>>TSTATE_CCR_SHIFT)&PSR_ICC; 363 if (instr.i_fmovcc.i_cond != cond) return(0); /* success */ 364 rs1 = fs->fs_regs[rs2]; 365 goto mov; 366 case FMVXC >> 2: 367 /* Presume we're curproc */ 368 DPRINTF(FPE_INSN, ("fpu_execute: FMVXC\n")); 369 cond = (curproc->p_md.md_tf->tf_tstate>>(TSTATE_CCR_SHIFT+XCC_SHIFT))&PSR_ICC; 370 if (instr.i_fmovcc.i_cond != cond) return(0); /* success */ 371 rs1 = fs->fs_regs[rs2]; 372 goto mov; 373 case FMVRZ >> 2: 374 /* Presume we're curproc */ 375 DPRINTF(FPE_INSN, ("fpu_execute: FMVRZ\n")); 376 rs1 = instr.i_fmovr.i_rs1; 377 if (rs1 != 0 && (int64_t)curproc->p_md.md_tf->tf_global[rs1] != 0) 378 return (0); /* success */ 379 rs1 = fs->fs_regs[rs2]; 380 goto mov; 381 case FMVRLEZ >> 2: 382 /* Presume we're curproc */ 383 DPRINTF(FPE_INSN, ("fpu_execute: FMVRLEZ\n")); 384 rs1 = instr.i_fmovr.i_rs1; 385 if (rs1 != 0 && (int64_t)curproc->p_md.md_tf->tf_global[rs1] > 0) 386 return (0); /* success */ 387 rs1 = fs->fs_regs[rs2]; 388 goto mov; 389 case FMVRLZ >> 2: 390 /* Presume we're curproc */ 391 DPRINTF(FPE_INSN, ("fpu_execute: FMVRLZ\n")); 392 rs1 = instr.i_fmovr.i_rs1; 393 if (rs1 == 0 || (int64_t)curproc->p_md.md_tf->tf_global[rs1] >= 0) 394 return (0); /* success */ 395 rs1 = fs->fs_regs[rs2]; 396 goto mov; 397 case FMVRNZ >> 2: 398 /* Presume we're curproc */ 399 DPRINTF(FPE_INSN, ("fpu_execute: FMVRNZ\n")); 400 rs1 = instr.i_fmovr.i_rs1; 401 if (rs1 == 0 || (int64_t)curproc->p_md.md_tf->tf_global[rs1] == 0) 402 return (0); /* success */ 403 rs1 = fs->fs_regs[rs2]; 404 goto mov; 405 case FMVRGZ >> 2: 406 /* Presume we're curproc */ 407 DPRINTF(FPE_INSN, ("fpu_execute: FMVRGZ\n")); 408 rs1 = instr.i_fmovr.i_rs1; 409 if (rs1 == 0 || (int64_t)curproc->p_md.md_tf->tf_global[rs1] <= 0) 410 return (0); /* success */ 411 rs1 = fs->fs_regs[rs2]; 412 goto mov; 413 case FMVRGEZ >> 2: 414 /* Presume we're curproc */ 415 DPRINTF(FPE_INSN, ("fpu_execute: FMVRGEZ\n")); 416 rs1 = instr.i_fmovr.i_rs1; 417 if (rs1 != 0 && (int64_t)curproc->p_md.md_tf->tf_global[rs1] < 0) 418 return (0); /* success */ 419 rs1 = fs->fs_regs[rs2]; 420 goto mov; 421 default: 422 DPRINTF(FPE_INSN, 423 ("fpu_execute: unknown v9 FP inst %x opf %x\n", 424 instr.i_int, opf)); 425 return (NOTFPU); 426 } 427 } 428 #endif /* SUN4U */ 429 switch (opf >>= 2) { 430 431 default: 432 DPRINTF(FPE_INSN, 433 ("fpu_execute: unknown basic FP inst %x opf %x\n", 434 instr.i_int, opf)); 435 return (NOTFPU); 436 437 case FMOV >> 2: /* these should all be pretty obvious */ 438 DPRINTF(FPE_INSN, ("fpu_execute: FMOV\n")); 439 rs1 = fs->fs_regs[rs2]; 440 goto mov; 441 442 case FNEG >> 2: 443 DPRINTF(FPE_INSN, ("fpu_execute: FNEG\n")); 444 rs1 = fs->fs_regs[rs2] ^ (1 << 31); 445 goto mov; 446 447 case FABS >> 2: 448 DPRINTF(FPE_INSN, ("fpu_execute: FABS\n")); 449 rs1 = fs->fs_regs[rs2] & ~(1 << 31); 450 mov: 451 #ifndef SUN4U 452 fs->fs_regs[rd] = rs1; 453 #else /* SUN4U */ 454 i = 1<<(type-1); 455 fs->fs_regs[rd++] = rs1; 456 while (--i > 0) 457 fs->fs_regs[rd++] = fs->fs_regs[++rs2]; 458 #endif /* SUN4U */ 459 fs->fs_fsr = fe->fe_fsr; 460 return (0); /* success */ 461 462 case FSQRT >> 2: 463 DPRINTF(FPE_INSN, ("fpu_execute: FSQRT\n")); 464 fpu_explode(fe, &fe->fe_f1, type, rs2); 465 fp = fpu_sqrt(fe); 466 break; 467 468 case FADD >> 2: 469 DPRINTF(FPE_INSN, ("fpu_execute: FADD\n")); 470 fpu_explode(fe, &fe->fe_f1, type, rs1); 471 fpu_explode(fe, &fe->fe_f2, type, rs2); 472 fp = fpu_add(fe); 473 break; 474 475 case FSUB >> 2: 476 DPRINTF(FPE_INSN, ("fpu_execute: FSUB\n")); 477 fpu_explode(fe, &fe->fe_f1, type, rs1); 478 fpu_explode(fe, &fe->fe_f2, type, rs2); 479 fp = fpu_sub(fe); 480 break; 481 482 case FMUL >> 2: 483 DPRINTF(FPE_INSN, ("fpu_execute: FMUL\n")); 484 fpu_explode(fe, &fe->fe_f1, type, rs1); 485 fpu_explode(fe, &fe->fe_f2, type, rs2); 486 fp = fpu_mul(fe); 487 break; 488 489 case FDIV >> 2: 490 DPRINTF(FPE_INSN, ("fpu_execute: FDIV\n")); 491 fpu_explode(fe, &fe->fe_f1, type, rs1); 492 fpu_explode(fe, &fe->fe_f2, type, rs2); 493 fp = fpu_div(fe); 494 break; 495 496 case FCMP >> 2: 497 DPRINTF(FPE_INSN, ("fpu_execute: FCMP\n")); 498 fpu_explode(fe, &fe->fe_f1, type, rs1); 499 fpu_explode(fe, &fe->fe_f2, type, rs2); 500 fpu_compare(fe, 0); 501 goto cmpdone; 502 503 case FCMPE >> 2: 504 DPRINTF(FPE_INSN, ("fpu_execute: FCMPE\n")); 505 fpu_explode(fe, &fe->fe_f1, type, rs1); 506 fpu_explode(fe, &fe->fe_f2, type, rs2); 507 fpu_compare(fe, 1); 508 cmpdone: 509 /* 510 * The only possible exception here is NV; catch it 511 * early and get out, as there is no result register. 512 */ 513 cx = fe->fe_cx; 514 fsr = fe->fe_fsr | (cx << FSR_CX_SHIFT); 515 if (cx != 0) { 516 if (fsr & (FSR_NV << FSR_TEM_SHIFT)) { 517 fs->fs_fsr = (fsr & ~FSR_FTT) | 518 (FSR_TT_IEEE << FSR_FTT_SHIFT); 519 return (FPE); 520 } 521 fsr |= FSR_NV << FSR_AX_SHIFT; 522 } 523 fs->fs_fsr = fsr; 524 return (0); 525 526 case FSMULD >> 2: 527 case FDMULX >> 2: 528 DPRINTF(FPE_INSN, ("fpu_execute: FSMULx\n")); 529 if (type == FTYPE_EXT) 530 return (NOTFPU); 531 fpu_explode(fe, &fe->fe_f1, type, rs1); 532 fpu_explode(fe, &fe->fe_f2, type, rs2); 533 type++; /* single to double, or double to quad */ 534 fp = fpu_mul(fe); 535 break; 536 537 #ifdef SUN4U 538 case FXTOS >> 2: 539 case FXTOD >> 2: 540 case FXTOQ >> 2: 541 DPRINTF(FPE_INSN, ("fpu_execute: FXTOx\n")); 542 type = FTYPE_LNG; 543 fpu_explode(fe, fp = &fe->fe_f1, type, rs2); 544 type = opf & 3; /* sneaky; depends on instruction encoding */ 545 break; 546 547 case FTOX >> 2: 548 DPRINTF(FPE_INSN, ("fpu_execute: FTOx\n")); 549 fpu_explode(fe, fp = &fe->fe_f1, type, rs2); 550 type = FTYPE_LNG; 551 break; 552 #endif /* SUN4U */ 553 554 case FTOI >> 2: 555 case FTOS >> 2: 556 case FTOD >> 2: 557 case FTOQ >> 2: 558 DPRINTF(FPE_INSN, ("fpu_execute: FTOx\n")); 559 fpu_explode(fe, fp = &fe->fe_f1, type, rs2); 560 type = opf & 3; /* sneaky; depends on instruction encoding */ 561 break; 562 } 563 564 /* 565 * ALU operation is complete. Collapse the result and then check 566 * for exceptions. If we got any, and they are enabled, do not 567 * alter the destination register, just stop with an exception. 568 * Otherwise set new current exceptions and accrue. 569 */ 570 fpu_implode(fe, fp, type, space); 571 cx = fe->fe_cx; 572 fsr = fe->fe_fsr; 573 if (cx != 0) { 574 mask = (fsr >> FSR_TEM_SHIFT) & FSR_TEM_MASK; 575 if (cx & mask) { 576 /* not accrued??? */ 577 fs->fs_fsr = (fsr & ~FSR_FTT) | 578 (FSR_TT_IEEE << FSR_FTT_SHIFT) | 579 (cx_to_trapx[(cx & mask) - 1] << FSR_CX_SHIFT); 580 return (FPE); 581 } 582 fsr |= (cx << FSR_CX_SHIFT) | (cx << FSR_AX_SHIFT); 583 } 584 fs->fs_fsr = fsr; 585 fs->fs_regs[rd] = space[0]; 586 if (type >= FTYPE_DBL || type == FTYPE_LNG) { 587 fs->fs_regs[rd + 1] = space[1]; 588 if (type > FTYPE_DBL) { 589 fs->fs_regs[rd + 2] = space[2]; 590 fs->fs_regs[rd + 3] = space[3]; 591 } 592 } 593 return (0); /* success */ 594 } 595