1; SF format is: 2; 3; [sign] 1.[23bits] E[8bits(n-127)] 4; 5; SEEEEEEE Emmmmmmm mmmmmmmm mmmmmmmm 6; 7; [A+0] mmmmmmmm 8; [A+1] mmmmmmmm 9; [A+2] Emmmmmmm 10; [A+3] SEEEEEEE 11; 12; Special values (xxx != 0): 13; 14; s1111111 10000000 00000000 00000000 infinity 15; s1111111 1xxxxxxx xxxxxxxx xxxxxxxx NaN 16; s0000000 00000000 00000000 00000000 zero 17; s0000000 0xxxxxxx xxxxxxxx xxxxxxxx denormals 18; 19; Note that CMPtype is "signed char" for rl78 20; 21 22#include "vregs.h" 23 24#define Z PSW.6 25 26START_FUNC ___negsf2 27 28 ;; Negate the floating point value. 29 ;; Input at [SP+4]..[SP+7]. 30 ;; Output to R8..R11. 31 32 movw ax, [SP+4] 33 movw r8, ax 34 movw ax, [SP+6] 35 xor a, #0x80 36 movw r10, ax 37 ret 38 39END_FUNC ___negsf2 40 41;; ------------------internal functions used by later code -------------- 42 43START_FUNC __int_isnan 44 45 ;; [HL] points to value, returns Z if it's a NaN 46 47 mov a, [hl+2] 48 and a, #0x80 49 mov x, a 50 mov a, [hl+3] 51 and a, #0x7f 52 cmpw ax, #0x7f80 53 skz 54 ret ; return NZ if not NaN 55 mov a, [hl+2] 56 and a, #0x7f 57 or a, [hl+1] 58 or a, [hl] 59 bnz $1f 60 clr1 Z ; Z, normal 61 ret 621: 63 set1 Z ; nan 64 ret 65 66END_FUNC __int_isnan 67 68START_FUNC __int_eithernan 69 70 ;; call from toplevel functions, returns Z if either number is a NaN, 71 ;; or NZ if both are OK. 72 73 movw ax, sp 74 addw ax, #8 75 movw hl, ax 76 call $!__int_isnan 77 bz $1f 78 79 movw ax, sp 80 addw ax, #12 81 movw hl, ax 82 call $!__int_isnan 831: 84 ret 85 86END_FUNC __int_eithernan 87 88START_FUNC __int_iszero 89 90 ;; [HL] points to value, returns Z if it's zero 91 92 mov a, [hl+3] 93 and a, #0x7f 94 or a, [hl+2] 95 or a, [hl+1] 96 or a, [hl] 97 ret 98 99END_FUNC __int_iszero 100 101START_FUNC __int_cmpsf 102 103 ;; This is always called from some other function here, 104 ;; so the stack offsets are adjusted accordingly. 105 106 ;; X [SP+8] <=> Y [SP+12] : <a> <=> 0 107 108 movw ax, sp 109 addw ax, #8 110 movw hl, ax 111 call $!__int_iszero 112 bnz $1f 113 114 movw ax, sp 115 addw ax, #12 116 movw hl, ax 117 call $!__int_iszero 118 bnz $2f 119 ;; At this point, both args are zero. 120 mov a, #0 121 ret 122 1232: 124 movw ax, sp 125 addw ax, #8 126 movw hl, ax 1271: 128 ;; At least one arg is non-zero so we can just compare magnitudes. 129 ;; Args are [HL] and [HL+4]. 130 131 mov a, [HL+3] 132 xor a, [HL+7] 133 mov1 cy, a.7 134 bnc $1f 135 136 mov a, [HL+3] 137 sar a, 7 138 or a, #1 139 ret 140 1411: ;; Signs the same, compare magnitude. It's safe to lump 142 ;; the sign bits, exponent, and mantissa together here, since they're 143 ;; stored in the right sequence. 144 movw ax, [HL+2] 145 cmpw ax, [HL+6] 146 bc $ybig_cmpsf ; branch if X < Y 147 bnz $xbig_cmpsf ; branch if X > Y 148 149 movw ax, [HL] 150 cmpw ax, [HL+4] 151 bc $ybig_cmpsf ; branch if X < Y 152 bnz $xbig_cmpsf ; branch if X > Y 153 154 mov a, #0 155 ret 156 157xbig_cmpsf: ; |X| > |Y| so return A = 1 if pos, 0xff if neg 158 mov a, [HL+3] 159 sar a, 7 160 or a, #1 161 ret 162ybig_cmpsf: ; |X| < |Y| so return A = 0xff if pos, 1 if neg 163 mov a, [HL+3] 164 xor a, #0x80 165 sar a, 7 166 or a, #1 167 ret 168 169END_FUNC __int_cmpsf 170 171;; ---------------------------------------------------------- 172 173START_FUNC ___cmpsf2 174 ;; This functions calculates "A <=> B". That is, if A is less than B 175 ;; they return -1, if A is greater than B, they return 1, and if A 176 ;; and B are equal they return 0. If either argument is NaN the 177 ;; behaviour is undefined. 178 179 ;; Input at [SP+4]..[SP+7]. 180 ;; Output to R8..R9. 181 182 call $!__int_eithernan 183 bnz $1f 184 movw r8, #1 185 ret 1861: 187 call $!__int_cmpsf 188 mov r8, a 189 sar a, 7 190 mov r9, a 191 ret 192 193END_FUNC ___cmpsf2 194 195;; ---------------------------------------------------------- 196 197 ;; These functions are all basically the same as ___cmpsf2 198 ;; except that they define how they handle NaNs. 199 200START_FUNC ___eqsf2 201 ;; Returns zero iff neither argument is NaN 202 ;; and both arguments are equal. 203START_ANOTHER_FUNC ___nesf2 204 ;; Returns non-zero iff either argument is NaN or the arguments are 205 ;; unequal. Effectively __nesf2 is the same as __eqsf2 206START_ANOTHER_FUNC ___lesf2 207 ;; Returns a value less than or equal to zero if neither 208 ;; argument is NaN, and the first is less than or equal to the second. 209START_ANOTHER_FUNC ___ltsf2 210 ;; Returns a value less than zero if neither argument is 211 ;; NaN, and the first is strictly less than the second. 212 213 ;; Input at [SP+4]..[SP+7]. 214 ;; Output to R8. 215 216 mov r8, #1 217 218;;; Fall through 219 220START_ANOTHER_FUNC __int_cmp_common 221 222 call $!__int_eithernan 223 sknz 224 ;; return value (pre-filled-in below) for "either is nan" 225 ret 226 227 call $!__int_cmpsf 228 mov r8, a 229 ret 230 231END_ANOTHER_FUNC __int_cmp_common 232END_ANOTHER_FUNC ___ltsf2 233END_ANOTHER_FUNC ___lesf2 234END_ANOTHER_FUNC ___nesf2 235END_FUNC ___eqsf2 236 237START_FUNC ___gesf2 238 ;; Returns a value greater than or equal to zero if neither argument 239 ;; is a NaN and the first is greater than or equal to the second. 240START_ANOTHER_FUNC ___gtsf2 241 ;; Returns a value greater than zero if neither argument 242 ;; is NaN, and the first is strictly greater than the second. 243 244 mov r8, #0xffff 245 br $__int_cmp_common 246 247END_ANOTHER_FUNC ___gtsf2 248END_FUNC ___gesf2 249 250;; ---------------------------------------------------------- 251 252START_FUNC ___unordsf2 253 ;; Returns a nonzero value if either argument is NaN, otherwise 0. 254 255 call $!__int_eithernan 256 movw r8, #0 257 sknz ; this is from the call, not the movw 258 movw r8, #1 259 ret 260 261END_FUNC ___unordsf2 262 263;; ---------------------------------------------------------- 264 265START_FUNC ___fixsfsi 266 ;; Converts its floating point argument into a signed long, 267 ;; rounding toward zero. 268 ;; The behaviour with NaNs and Infinities is not well defined. 269 ;; We choose to return 0 for NaNs, -INTMAX for -inf and INTMAX for +inf. 270 ;; This matches the behaviour of the C function in libgcc2.c. 271 272 ;; Input at [SP+4]..[SP+7], result is in (lsb) R8..R11 (msb). 273 274 ;; Special case handling for infinities as __fixunssfsi 275 ;; will not give us the values that we want. 276 movw ax, sp 277 addw ax, #4 278 movw hl, ax 279 call !!__int_isinf 280 bnz $1f 281 mov a, [SP+7] 282 bt a.7, $2f 283 ;; +inf 284 movw r8, #-1 285 movw r10, #0x7fff 286 ret 287 ;; -inf 2882: mov r8, #0 289 mov r10, #0x8000 290 ret 291 292 ;; Load the value into r10:r11:X:A 2931: movw ax, [SP+4] 294 movw r10, ax 295 movw ax, [SP+6] 296 297 ;; If the value is positive we can just use __fixunssfsi 298 bf a.7, $__int_fixunssfsi 299 300 ;; Otherwise we negate the value, call __fixunssfsi and 301 ;; then negate its result. 302 clr1 a.7 303 call $!__int_fixunssfsi 304 305 movw ax, #0 306 subw ax, r8 307 movw r8, ax 308 movw ax, #0 309 sknc 310 decw ax 311 subw ax, r10 312 movw r10, ax 313 314 ;; Check for a positive result (which should only happen when 315 ;; __fixunssfsi returns UINTMAX or 0). In such cases just return 0. 316 mov a, r11 317 bt a.7, $1f 318 movw r10,#0x0 319 movw r8, #0x0 320 3211: ret 322 323END_FUNC ___fixsfsi 324 325START_FUNC ___fixunssfsi 326 ;; Converts its floating point argument into an unsigned long 327 ;; rounding towards zero. Negative arguments all become zero. 328 ;; We choose to return 0 for NaNs and -inf, but UINTMAX for +inf. 329 ;; This matches the behaviour of the C function in libgcc2.c. 330 331 ;; Input at [SP+4]..[SP+7], result is in (lsb) R8..R11 (msb) 332 333 ;; Get the input value. 334 movw ax, [SP+4] 335 movw r10, ax 336 movw ax, [SP+6] 337 338 ;; Fall through into the internal function. 339 340 .global __int_fixunssfsi 341__int_fixunssfsi: 342 ;; Input in (lsb) r10.r11.x.a (msb). 343 344 ;; Test for a negative input. We shift the other bits at the 345 ;; same time so that A ends up holding the whole exponent: 346 ;; 347 ;; before: 348 ;; SEEEEEEE EMMMMMMM MMMMMMMM MMMMMMMM 349 ;; A X R11 R10 350 ;; 351 ;; after: 352 ;; EEEEEEEE MMMMMMM0 MMMMMMMM MMMMMMMM 353 ;; A X R11 R10 354 shlw ax, 1 355 bnc $1f 356 357 ;; Return zero. 3582: movw r8, #0 359 movw r10, #0 360 ret 361 362 ;; An exponent of -1 is either a NaN or infinity. 3631: cmp a, #-1 364 bnz $3f 365 ;; For NaN we return 0. For infinity we return UINTMAX. 366 mov a, x 367 or a, r10 368 or a, r11 369 cmp0 a 370 bnz $2b 371 3726: movw r8, #-1 ; -1 => UINT_MAX 373 movw r10, #-1 374 ret 375 376 ;; If the exponent is negative the value is < 1 and so the 377 ;; converted value is 0. Note we must allow for the bias 378 ;; applied to the exponent. Thus a value of 127 in the 379 ;; EEEEEEEE bits actually represents an exponent of 0, whilst 380 ;; a value less than 127 actually represents a negative exponent. 381 ;; Also if the EEEEEEEE bits are all zero then this represents 382 ;; either a denormal value or 0.0. Either way for these values 383 ;; we return 0. 3843: sub a, #127 385 bc $2b 386 387 ;; A now holds the bias adjusted exponent, which is known to be >= 0. 388 ;; If the exponent is > 31 then the conversion will overflow. 389 cmp a, #32 390 bnc $6b 3914: 392 ;; Save the exponent in H. We increment it by one because we want 393 ;; to be sure that the loop below will always execute at least once. 394 inc a 395 mov h, a 396 397 ;; Get the top 24 bits of the mantissa into A:X:R10 398 ;; Include the implicit 1-bit that is inherent in the IEEE fp format. 399 ;; 400 ;; before: 401 ;; EEEEEEEE MMMMMMM0 MMMMMMMM MMMMMMMM 402 ;; H X R11 R10 403 ;; after: 404 ;; EEEEEEEE 1MMMMMMM MMMMMMMM MMMMMMMM 405 ;; H A X R10 406 407 mov a, r11 408 xch a, x 409 shr a, 1 410 set1 a.7 411 412 ;; Clear B:C:R12:R13 413 movw bc, #0 414 movw r12, #0 415 416 ;; Shift bits from the mantissa (A:X:R10) into (B:C:R12:R13), 417 ;; decrementing the exponent as we go. 418 419 ;; before: 420 ;; MMMMMMMM MMMMMMMM MMMMMMMM xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx 421 ;; A X R10 B C R12 R13 422 ;; first iter: 423 ;; MMMMMMMM MMMMMMMM MMMMMMM0 xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxM 424 ;; A X R10 B C R12 R13 425 ;; second iter: 426 ;; MMMMMMMM MMMMMMMM MMMMMM00 xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxMM 427 ;; A X R10 B C R12 R13 428 ;; etc. 4295: 430 xch a, r10 431 shl a, 1 432 xch a, r10 433 434 rolwc ax, 1 435 436 xch a, r13 437 rolc a, 1 438 xch a, r13 439 440 xch a, r12 441 rolc a, 1 442 xch a, r12 443 444 rolwc bc, 1 445 446 dec h 447 bnz $5b 448 449 ;; Result is currently in (lsb) r13.r12. c. b. (msb), 450 ;; Move it into (lsb) r8. r9. r10. r11 (msb). 451 452 mov a, r13 453 mov r8, a 454 455 mov a, r12 456 mov r9, a 457 458 mov a, c 459 mov r10, a 460 461 mov a, b 462 mov r11, a 463 464 ret 465 466END_FUNC ___fixunssfsi 467 468;; ------------------------------------------------------------------------ 469 470START_FUNC ___floatsisf 471 ;; Converts its signed long argument into a floating point. 472 ;; Argument in [SP+4]..[SP+7]. Result in R8..R11. 473 474 ;; Get the argument. 475 movw ax, [SP+4] 476 movw bc, ax 477 movw ax, [SP+6] 478 479 ;; Test the sign bit. If the value is positive then drop into 480 ;; the unsigned conversion routine. 481 bf a.7, $2f 482 483 ;; If negative convert to positive ... 484 movw hl, ax 485 movw ax, #0 486 subw ax, bc 487 movw bc, ax 488 movw ax, #0 489 sknc 490 decw ax 491 subw ax, hl 492 493 ;; If the result is negative then the input was 0x80000000 and 494 ;; we want to return -0.0, which will not happen if we call 495 ;; __int_floatunsisf. 496 bt a.7, $1f 497 498 ;; Call the unsigned conversion routine. 499 call $!__int_floatunsisf 500 501 ;; Negate the result. 502 set1 r11.7 503 504 ;; Done. 505 ret 506 5071: ;; Return -0.0 aka 0xcf000000 508 509 clrb a 510 mov r8, a 511 mov r9, a 512 mov r10, a 513 mov a, #0xcf 514 mov r11, a 515 ret 516 517START_ANOTHER_FUNC ___floatunsisf 518 ;; Converts its unsigned long argument into a floating point. 519 ;; Argument in [SP+4]..[SP+7]. Result in R8..R11. 520 521 ;; Get the argument. 522 movw ax, [SP+4] 523 movw bc, ax 524 movw ax, [SP+6] 525 5262: ;; Internal entry point from __floatsisf 527 ;; Input in AX (high) and BC (low) 528 .global __int_floatunsisf 529__int_floatunsisf: 530 531 ;; Special case handling for zero. 532 cmpw ax, #0 533 bnz $1f 534 movw ax, bc 535 cmpw ax, #0 536 movw ax, #0 537 bnz $1f 538 539 ;; Return 0.0 540 movw r8, ax 541 movw r10, ax 542 ret 543 5441: ;; Pre-load the loop count/exponent. 545 ;; Exponents are biased by 0x80 and we start the loop knowing that 546 ;; we are going to skip the highest set bit. Hence the highest value 547 ;; that we can get for the exponent is 0x1e (bits from input) + 0x80 = 0x9e. 548 mov h, #0x9e 549 550 ;; Move bits off the top of AX:BC until we hit a 1 bit. 551 ;; Decrement the count of remaining bits as we go. 552 5532: shlw bc, 1 554 rolwc ax, 1 555 bc $3f 556 dec h 557 br $2b 558 559 ;; Ignore the first one bit - it is implicit in the IEEE format. 560 ;; The count of remaining bits is the exponent. 561 562 ;; Assemble the final floating point value. We have... 563 ;; before: 564 ;; EEEEEEEE MMMMMMMM MMMMMMMM MMMMMMMM xxxxxxxx 565 ;; H A X B C 566 ;; after: 567 ;; 0EEEEEEE EMMMMMMM MMMMMMMM MMMMMMMM 568 ;; R11 R10 R9 R8 569 570 5713: shrw ax, 1 572 mov r10, a 573 mov a, x 574 mov r9, a 575 576 mov a, b 577 rorc a, 1 578 579 ;; If the bottom bit of B was set before we shifted it out then we 580 ;; need to round the result up. Unless none of the bits in C are set. 581 ;; In this case we are exactly half-way between two values, and we 582 ;; round towards an even value. We round up by increasing the 583 ;; mantissa by 1. If this results in a zero mantissa we have to 584 ;; increment the exponent. We round down by ignoring the dropped bits. 585 586 bnc $4f 587 cmp0 c 588 sknz 589 bf a.0, $4f 590 5915: ;; Round the mantissa up by 1. 592 add a, #1 593 addc r9, #0 594 addc r10, #0 595 bf r10.7, $4f 596 inc h 597 clr1 r10.7 598 5994: mov r8, a 600 mov a, h 601 shr a, 1 602 mov r11, a 603 sknc 604 set1 r10.7 605 ret 606 607END_ANOTHER_FUNC ___floatunsisf 608END_FUNC ___floatsisf 609