Returning a floating-point result from the RISC-V assembler


#1

When I developed the RISC-V assembler I also implemented some of the floating-point instructions. Here’s an example of how you could use them to perform floating-point calculations and return a floating-point result.

First evaluate the original assembler source file: RISC-V assembler in uLisp

Then add the following definitions: RISC-V assembler floating-point extensions

This file redefines the function regno to handle the floating-point registers, and adds definitions for the most useful floating-point instructions.

Calling a machine-code function from uLisp returns a Lisp integer value, but you can return values of other types by passing a list to the call, and putting the result back into one of the list cells.

The following example takes a list of floating-point numbers. It multiplies them together, and then puts the floating-point result back into the first element of the list:

(defcode product (x)
  ($li 'a2 1)
  ($fcvt.s.w 'fa1 'a2)
  ($mv 'a2 'a0)
  repeat
  ($beqz 'a0 finished)
  ($ld 'a1 0 '(a0))
  ($flw 'fa0 8 '(a1))
  ($fmul.s 'fa1 'fa0 'fa1)
  ($ld 'a0 8 '(a0))
  ($j repeat)
  finished
  ($ld 'a1 0 '(a2))
  ($fsw 'fa1 8 '(a1))
  ($ret))

Here’s an example of using it. First define a list of the numbers to be multiplied:

> (defvar lst '(1.2 2.3 3.4))
lst

Call the machine-code function:

> (product lst)
0

It returns the value of a0, which is always 0, but the result is put back in the first element of the list:

> lst
(9.384 2.3 3.4)

Here’s how it works. The list passed to the machine-code routine looks like this:

First fa1 is set to the floating-point value 1.0 by:

($li 'a2 1)
($fcvt.s.w 'fa1 'a2)

The pointer to the list was passed in a0, and this is copied into a2. Then a1 is set to the address of the first element of the list by:

($ld 'a1 0 '(a0))

and the floating-point value 1.2 in the cdr of the object, which has offset 8 (because the K210 is a 64-bit processor), is copied into fa0 by:

($flw 'fa0 8 '(a1))

Register fa1 is multiplied by this, and the process is repeated for the remaining elements in the list, until a0 is zero indicating that we’ve reached the nil at the end of the list.

Finally fa1 is put back into value cell of the first element with:

($ld 'a1 0 '(a2))
($fsw 'fa1 8 '(a1))

RISC assembler and list