RISC assembler and list


#1

Hello,
I’m reading the examples of RISC assembler and list handling.
On the web page (http://www.ulisp.com/show?30ZU), I think there’s a typo because the CAR/CDR example uses ARM mnemonic, not the RISC ones.
So I suppose that part should read,

This loads the CAR of the list in A1
CAR : ($ld 'a1 0 'a0)

This loads the CDR of the list in A1
CDR: ($ld 'a1 8 'a0)

Looking at the “product” example, is it possible to have some clarifications?
In the first part of the code,

[…]
($ld 'a1 0 '(a0))
this copies the CAR of the list in a1, correct?

($ld 'a1 8 '(a1))
if my first assumption is correct, this I cannot understand. it’s CDR of CAR (?!?!)

($mul 'a2 'a1 'a2)
this performs the multiplication a1 by a2 (a2 initially is 1).

($ld 'a0 8 '(a0))
this load the CDR of the list in a0

Please, can you clarify this for me?

Thanks a lot,
Fausto


#2

All objects in uLisp are boxed - they are stored in a cons cell, where the car is a type identifier and cdr is the value itself. The initial value is a pointer to a singly-linked list where the cars are cons cells whose cdrs are the numbers you want. So, yes, this takes the cdr of a car.


#3

You were correct - it had ARM mnemonics. Thanks for pointing this out, and I’ve fixed it now.


#4

Thanks a lot! Now it’s perfectly clear!


#5

Why do we not also put the table for the floating-point mnemonics used in the Mandelbrot example?
They could be quite useful in the future.


#6

Just thinking about rewriting the small complex number library (used in the FFT example) in assembler, but I have a problem with the return value, that, by design, is always a number, if I understood correctly.

Is there a way to return a memory address pointing a list, instead?


#7

That’s a limitation of the assembler at present - the result it returns is converted to a Lisp integer, and there’s no way to return an address/pointer. I couldn’t see a way of returning a type with the result.

You could change the assembler to always return a pointer; if you wanted to return a number you’d have to pack it into an object with a number tag in the car.


#8

The main question there is where you’d get hold of a cons cell to do that. You’d have to pass that in as an argument too, wouldn’t you?


#9

Good point. The simplest solution would be to pass the assembler routine a list of the right size; it could then modify the list, and you’d ignore the returned integer.


#10

I have two different solutions to that issue, actually.

The first is, obviously, that if immediate values are added it becomes possible to stuff a typed value in a single pointer without ever needing the cons cell. It doesn’t give you all the bits, though, and is useless for floats.

The second approach is to make it possible to call uLisp functions from assembler and then just call cons. I’m not sure this is much less involved than the immediates, though.


#11

I thought about that, but couldn’t figure out a good way to do it.


#12

I don’t know about ‘good’, but the first step seems to be letting built-in symbols be used as labels. That alone would probably make it possible. You can build the argument list on the stack and pass a NULL environment. It gets easier if we change the calling convention, so that you don’t need to build the list if you’re calling a function that only takes a couple of arguments. Then the function called doesn’t need to unpack the list, either.

That said, I think there’s substantial value in the ability to do simple “number(s) in, number out” assembly routines, so I don’t quite know…


#13

The existing approach is quite ok. The problem resides in the complex numbers, even if they are still numbers, they are quite special :)


#14

This discussion made be realize that in the current approach there really isn’t a way for assembler code to call any other functions.

Probably you want to consider a means for functions generated by assembly code to call other functions, both those builtin and those generated by the assembler. Ditto for the C-code, it can call other C-code, but ought to be able to call assembler functions?

Once you have the various possibilities covered, it ought to be possible to write a compiler for uLisp, if that is desirable.


#15

That’s true - and it’s a much harder problem to solve properly than it looks at first. (Well, at least that turned out to be the case for me.)

This is both trivial and complicated, annoyingly enough. At the basic level, the problem is that uLisp uses a very specific way to call its user-visible functions, and that way relies on all the arguments being available as a uLisp list. That results in a catch-22: We can’t call the allocator from assembly, because in order to do so we need to already have allocated some objects for it to accept as arguments. Fun stuff.

The C code is all working at compile time. The assembler functions are created at runtime. The C code can (and, indeed, does) call an assembler function if it’s given a pointer to one. In much the same way, the C code can call a uLisp interpreted function. It just needs to be handed a pointer to one, because it has absolutely no way of getting a handle to one at compile time.

I’ll go further; it should be possible to write uLisp mostly in uLisp. There are quite a few steps to take, but they all seem feasible to me, and most of them are needed for the compiler, too.


#16

I just received my Maixduino last week and uLisp was among the first things I tried (after the basic setup and testing the camera and display). I was curious about the risc-v assembler since that was a motivation for buying the Maixduino. But I am yet to look at the source code for uLisp , so I appreciate your comments.


#17

I’ve written an example of returning a floating-point value from a RISC-V machine-code function in case its useful:


#18

Using this approach, it’s also possible to implement complex numbers calculation in assembler, passing three lists two for the operands and one (filled with 0s for example) for the result. A similar approach was used in FORTRAN, for example.
Clever idea! :)