; float.h -- standard Hawk definitions for floating point ; language: SMAL32 assembly language, intended as an include file ; author: Douglas W. Jones ; date: Dec 1, 2019 ; revised: Jan 2, 2024 -- made compatible with revised hawk.h ; ; example: at the head of a smal 32 source file, include this line: ; | ; | USE "float.h" ; | ; --------------------- ; floating coprocessor registers FPLOW = qCOPROCREGq+1 FPA0 = qCOPROCREGq+2 FPA1 = qCOPROCREGq+3 ; --------------------- ; COSTAT bits and fields FPENAB = #0002 FPSEL = #0100 FPLONG = #1000 FPSHORT = #0000 FPENBIT = 1 ; --------------------- ; floating point operations on COSET (add to dest FP register) FPINT = 2 FPSQRT = 4 FPADD = 6 FPSUB = 8 FPMUL = 10 FPDIV = 12 ; --------------------- ; floating point operations on COGET (add to src FP register) FPNEG = 2 FPABS = 4 ; --------------------- ; floating point operations in Hawk monitor EXT FTOI ; float to int conversion ; takes R3 = f, floating point number ; wipes out R4-5 ; uses no stack ; returns R3 = i, the truncated integer equivalent ; R3 = #80000000 is used when f is too big ; C equivalent: i = (int) f ; --------------------- ; support macros for single floating point conversions ; convention: all internal names are of form qFPxxxxq ; intent: None of the following macros should be called ; from outside this package ; ; qFPVALq - the binary value, eventually the entire floating value ; accumulated as an integer, so 123.456 accumulates as 123456 ; normalized as 00.00000000...000 (30 bits left of the point) ; finally assembled into the number ; qFPSCLq - decimal scale factor, zero unless fractional or exponent part ; qFPSGNq - the sign, 0 = positive, -1 = negative ; qFPEXPq - the exponent. ; parser: skip leading + or - sign, record sign MACRO qFPSIGNq (a), (b) IF "a" = "-" qFPSGNq = -1 qFPINTq (b):1:1, (b):2:LEN(b)-1 ELSEIF "a" = "+" qFPINTq (b):1:1, (b):2:LEN(b)-1 ELSE qFPINTq (a), (b) ENDIF ENDMAC ; parser: recursively accumulate digits of integer part MACRO qFPINTq (a), (b) IF ("a" < "0")!("a" > "9") qFPOINTq (a), (b) ELSE qFPVALq = (((qFPVALq << 2) + qFPVALq) << 1) + a IF qFPVALq < 0 ERROR "a'b" too many digits ELSE qFPINTq (b):1:1, (b):2:LEN(b)-1 ENDIF ENDIF ENDMAC ; parser: if there's a point, get the fractional part MACRO qFPOINTq (a), (b) IF "a" = "." qFPFRACq (b):1:1, (b):2:LEN(b)-1 ELSE qFPEXPEq (a), (b) ENDIF ENDMAC ; parser: recursively accumulate the fractional part MACRO qFPFRACq (a), (b) IF ("a" < "0")!("a" > "9") qFPEXPEq (a), (b) ELSE qFPVALq = (((qFPVALq << 2) + qFPVALq) << 1) + a qFPSCLq = qFPSCLq - 1; IF qFPVALq < 0 ERROR "a'b" too many digits ELSE qFPFRACq (b):1:1, (b):2:LEN(b)-1 ENDIF ENDIF ENDMAC ; parser: if there's an e or E, get the exponent MACRO qFPEXPEq (a), (b) IF ("a" = "e")!("a" = "E") qFPEXPSq (b):1:1, (b):2:LEN(b)-1 ELSEIF ~("a" = "") ERROR "a'b" extra characters in mantissa ENDIF ENDMAC ; parser: if theres a + or -, handle the exponent sign MACRO qFPEXPSq (a), (b) qFPEXPq = 0 IF "a" = "-" qFPEXPVq (b):1:1, (b):2:LEN(b)-1 qFPSCLq = qFPSCLq - qFPEXPq ELSEIF "a" = "+" qFPEXPVq (b):1:1, (b):2:LEN(b)-1 qFPSCLq = qFPSCLq + qFPEXPq ELSE qFPEXPVq (a), (b) qFPSCLq = qFPSCLq + qFPEXPq ENDIF ENDMAC ; parser: recursively accumulate the exponent value MACRO qFPEXPVq (a), (b) IF ("a" < "0")!("a" > "9") IF ~("a" = "") ERROR "a'b" extra characters in exponent ENDIF ELSE qFPEXPq = (((qFPEXPq << 2) + qFPEXPq) << 1) + a qFPEXPVq (b):1:1, (b):2:LEN(b)-1 ENDIF ENDMAC ; normalize FPVAL FPEXP (31 bit mantissa, 2's comp exponent) MACRO qFPNORMq IF qFPVALq < #40000000 qFPVALq = qFPVALq << 1 qFPEXPq = qFPEXPq - 1 qFPNORMq ENDIF ENDMAC ; recursively decrement SCALE to zero while multiplying FPVAL FPEXP by 10 ; this is tricky! *10 => FPVAL * 1.25, EXP + 3, since 8*1.25 = 10 ; all this is done post normalize, so a renormalize step is included. MACRO qFPSCDNq qFPVALq = qFPVALq + (qFPVALq >>> 2) qFPSCLq = qFPSCLq - 1 qFPEXPq = qFPEXPq + 3 IF qFPVALq < 0 qFPVALq = qFPVALq >>> 1 qFPEXPq = qFPEXPq + 1 ENDIF IF qFPSCLq > 0 qFPSCDNq ENDIF ENDMAC ; recursively increment SCALE to zero while dividing FPVAL FPEXP by 10 ; this is tricky! /10 => FPVAL * 1.6, EXP - 4, since 1.6/16 = 0.1 ; all this is done post normalize, so a renormalize step is included. MACRO qFPSCUPq qFPVALq = ((qFPVALq >>> 1) + qFPVALq) qFPVALq = ((qFPVALq >>> 4) + qFPVALq) qFPVALq = ((qFPVALq >>> 8) + qFPVALq) qFPVALq = ((qFPVALq >>>16) + qFPVALq) qFPSCLq = qFPSCLq + 1 qFPEXPq = qFPEXPq - 4 IF qFPVALq < 0 qFPVALq = qFPVALq >>> 1 qFPEXPq = qFPEXPq + 1 ENDIF IF qFPSCLq < 0 qFPSCUPq ENDIF ENDMAC ; recursively denormalize until exponent within bounds MACRO qFPDENOq qFPVALq = qFPVALq >>> 1 qFPEXPq = qFPEXPq + 1 IF qFPEXPq < -126 qFPDENOq ENDIF ENDMAC ; --------------------- ; macro to convert text to single floating point ; qFPCONVq s ;where s = [+|-]int[.frac][(e|E)[+|-]exp] ; converted value left in qFPVALq ; uses above utility macros ; no other code should use these utility macros ; this is tricky! Accumulates value (FPVAL, FPSCL, FPEXP) ; then normalizes 31-bit mantissa and rounds to 24 places and renormalizes ; then deals with out-of-range exponents MACRO qFPCONVq s qFPVALq = 0 qFPSCLq = 0 qFPSGNq = 0 qFPSIGNq (s):1:1, (s):2:LEN(s)-1 qFPEXPq = 30 IF ~(qFPVALq = 0) qFPNORMq IF qFPSCLq > 0 qFPSCDNq ELSEIF qFPSCLq < 0 qFPSCUPq ENDIF ENDIF qFPVALq = qFPVALq + #40 IF qFPVALq < 0 qFPVALq = qFPVALq >>> 1 qFPEXPq = qFPEXPq + 1 ENDIF qFPVALq = qFPVALq >>> 7 IF qFPEXPq < -149 qFPVALq = 0 ELSEIF qFPEXPq > 126 qFPEXPq = 128 qFPVALq = #00400000 ENDIF IF (qFPEXPq < -126) & ~(qFPVALq = 0) qFPDENOq qFPEXPq = -127 ENDIF IF ~(qFPVALq = 0) qFPVALq = qFPVALq & #7FFFFF qFPEXPq = (qFPEXPq + #7F) << 23 qFPSGNq = qFPSGNq << 31 qFPVALq = qFPSGNq ! qFPEXPq ! qFPVALq ENDIF ENDMAC ; --------------------- ; macro to assemble a single precision IEEE floating point constant ; F s ;see qFPCONVq for constant format MACRO F s qFPCONVq s W qFPVALq ENDMAC ; --------------------- ; macro to load a single precision IEEE floating point constant ; LIW r,s ;see qFPCONVq for constant format MACRO LIF =r,s qFPCONVq s LIW r,qFPVALq ENDMAC END